Added restore functionality to GUI.
[FinalKeyGui.git] / src / fkgui / FkManager.java
1 package fkgui;
2
3 import java.awt.event.ActionEvent;
4 import java.awt.event.ActionListener;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.util.Collections;
10 import java.util.Comparator;
11 import java.util.Vector;
12
13 import jssc.SerialPort;
14
15 import org.eclipse.swt.widgets.Display;
16
17 import com.sun.corba.se.impl.ior.ByteBuffer;
18
19 import fkgui.FkActionEventListener.FkActionEvent;
20 import fkgui.FkActionEventListener.FkActionEventType;
21
22 public class FkManager implements ActionListener {
23         private static FkManager instance = null;
24         private SerialWorker com = null;
25         static final char ENTER_KEY = (char)13;
26         static final char PAUSE_CODE = (char)23; 
27         static final char SPACE_KEY = (char)32;
28         private Comparator<Account> sortMethod = null;
29         
30         public class Account
31         {
32                 public String name;
33                 public String num;
34                 private Boolean showNumInName;
35                 public Account( String acNum, String acName )
36                 {
37                         name = acName;
38                         num = acNum;
39                 }
40
41                 //Used by the ListView
42                 public String toString()
43                 {
44                         if( showNumInName )
45                         {
46                                 return( "["+num+"] "+name);
47                         }
48                         return(name);
49                 }
50
51                 public Account showNumInName(Boolean showAccountId) {
52                         showNumInName=showAccountId;
53                         return this;
54                 }
55         }
56                 
57         private Vector<Account> list;
58         private String banner = "Noname";
59         private String keyLayout = "USPC";
60         private Vector<String> supportedLayouts;
61         
62         protected FkManager()
63         {
64                 list = new Vector<Account>(256);
65                 supportedLayouts = new Vector<String>(4);
66         }
67         
68         public static FkManager getInstance()
69         {
70                 if( instance == null )
71                 {
72                         instance = new FkManager();
73                 }
74
75                 return( instance );
76         }
77         
78         public void setWorker( SerialWorker sw )
79         {
80                 com = sw;
81         }
82         
83
84         private class FkActionEventMsg implements Runnable  {
85                 private FkActionEventListener delegate;
86                 private FkActionEvent event;
87
88                 private FkActionEventMsg(FkActionEventListener d, FkActionEventType t, String data, Account acc, char act )
89                 {
90                         delegate=d;
91                         event=new FkActionEvent(t, data, acc,act) ;
92                 }
93                 @Override
94                 public void run() {
95                         if( delegate != null )
96                         {
97                                 delegate.fkActionEvent(event);
98                         }
99                 }
100         }
101         
102         
103         private Boolean checkState()
104         {
105                 int t=0;
106                 String msg="";
107                 Boolean stateOk=false;
108
109                 try {
110                         com.serialPort.writeByte((byte)SPACE_KEY);
111                         while( t < 100 )
112                         {
113                                 t++;
114                                 Thread.sleep(5);
115                                 
116                                 if( com.serialPort.getInputBufferBytesCount() > 0 )
117                                 {
118                                         msg += com.serialPort.readString();
119                                         if( msg.endsWith("\r\n>") )
120                                         {
121                                                 stateOk=true;
122                                                 msg="";
123                                                 break;
124                                         }
125                                 }
126                                 
127                         }
128                 
129                 } catch(Exception e)
130                 {
131                         stateOk=false;
132                 }
133                 flushSerial();
134                 if( !stateOk )
135                 {
136                         return(false);
137                 }
138                 return(true);
139                 
140         }
141         
142         private class TrigTask implements Runnable
143         {
144                 private Account acc;
145                 private char action;
146                 private FkActionEventListener delegate;
147
148                 public TrigTask(Account ac, char act, FkActionEventListener d )
149                 {
150                         acc=ac;
151                         action=act;
152                         delegate=d;
153                 }
154                 
155                 @Override
156                 public void run() {
157                         
158                         int t=0;
159                         String msg = "";
160
161
162                         try {
163                         
164                                 //First check that we get a prompt
165                                 if( !checkState() )
166                                 {
167                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.STATE_ERROR, "EXCEPTION",acc,action) );
168                                         return;
169                                 }
170
171                                 //Drain any previous input.
172                                 while( com.serialPort.getInputBufferBytesCount() > 0 )
173                                 {
174                                         com.serialPort.readBytes();
175                                 }
176                                 
177                                 //if it's not '%' we need to type which action.
178                                 if(action != '%' )
179                                 {
180         
181                                                 //If the action is to delete or override, we start by deleting the account number.
182                                                 //Those commands are xd or xo so we type an x before the action.
183                                                 if( action == 'd' || action == 'o' )
184                                                 {
185                                                         com.serialPort.writeByte((byte)'x');
186                                                 }
187                                                 com.serialPort.writeByte((byte)action);
188                                 }
189
190                                 com.serialPort.writeBytes(acc.num.toLowerCase().getBytes());
191                                 Thread.sleep(100);
192
193                                 //Verify that the device asks confirmation about the account number and action we requested, else return STATE_UNEXPECTED_ACTION_RESULT
194                                 //Wait until a ?\r\n is found
195                                 //Since we flushed the buffer, all we should see is an echo 
196                                 t=0;
197                                 while( t < 2 )
198                                 {
199                                         t++;
200                                         Thread.sleep(50);
201                                         if( com.serialPort.getInputBufferBytesCount() > 0 )
202                                         {
203                                                 msg += com.serialPort.readString();
204
205                                                 if( msg.contains(" ?\r\n") )
206                                                 {
207                                                         System.out.println("Got '"+msg+"'");
208
209                                                         String expect;
210
211                                                         if(action=='d'||action=='o')
212                                                         {
213                                                                 expect = "x"+action+"%"+acc.num.toLowerCase();
214
215                                                                 if(action=='d')
216                                                                 {
217                                                                         expect += "\r\nDel "+acc.name+" [y/n] ?\r\n";
218                                                                 } else {
219                                                                         expect += "\r\nOverride "+acc.name+" [y/n] ?\r\n";
220                                                                 }
221                                                         } else if(action=='%')
222                                                         {
223                                                                 expect=acc.num.toLowerCase()+"\r\n\r\n[U][S][P] "+acc.name+" ?\r\n";
224                                                         } else {
225                                                                 expect=""+action+"%"+acc.num.toLowerCase();
226                                                                 String actStr = (""+action).toUpperCase();
227                                                                 if(actStr.compareTo("S")==0)
228                                                                 {
229                                                                         actStr="SHOW";
230                                                                 }
231                                                                 expect += "\r\n\r\n\r\n["+actStr+"] "+acc.name+" ?\r\n";
232                                                         }
233
234                                                         String cut = msg.substring(0, expect.length());
235
236                                                         if( cut.compareTo(expect ) != 0 )
237                                                         {
238                                                                 System.out.println("Expected: '"+expect+"' Got: '"+cut+"'");
239                                                                 msg="Error: unexpected reply, disconnect it and close this program, this indicates either hardware-error or another program trying to communicate with it.";
240                                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.UNEXPECTED_ACTION_RESULT_ERROR, msg, acc, action) );
241                                                                 com.serialPort.writeByte((byte)9); //tab is unsupported in all inputs, should cause the device to go into a state where q will lock it.
242                                                                 com.serialPort.writeByte((byte)'q');
243                                                                 com.serialPort.closePort();
244                                                                 return;
245                                                         }
246                                                 }
247                                         }
248                                 }
249
250                                 if( action == 'd' || action == 'o' )
251                                 {
252                                         com.serialPort.writeByte((byte)'y');
253                                         Thread.sleep(100);
254                                 }
255
256                                 t=0;
257                                 int waitfor =  ((action=='o'||action=='d')?200:700);
258                                 while( t < waitfor )
259                                 {
260                                         t++;
261                                         Thread.sleep(50);
262                                         if( com.serialPort.getInputBufferBytesCount() > 0 )
263                                         {
264                                                 msg += com.serialPort.readString();
265                                                 //System.out.println( msg );
266                                                 if( msg.contains("[done]") || msg.contains("[deleted]") )
267                                                 {
268                                                         if( action == 'd' )
269                                                         {
270                                                                 list.remove( acc );
271                                                         }
272                                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_OKAY, msg, acc, action) );
273                                                         return;
274                                                 } else if( msg.contains("[abort]") )
275                                                 {
276                                                         //System.out.println("FkManager: Action Abort");;
277                                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ABORTED, msg,acc,action) );
278                                                         return;
279                                                 }
280                                         }
281                                 }
282                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ERROR, msg,acc,action) );
283
284                         } catch (Exception e1) {
285                                 System.out.println("TrigTask Exception:");
286                                 e1.printStackTrace();
287                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ERROR, "EXCEPTION",acc,action) );
288                         }
289                         
290                 }
291                 
292         }
293         
294         public void trig(Account acc, char action, FkActionEventListener delegate)
295         {
296                 TrigTask trigTask = new TrigTask(acc,action,delegate);
297                 new Thread(trigTask).start();
298         }
299         
300         
301         
302         private class SortByName implements Comparator<Account>
303         {
304                 @Override
305                 public int compare(Account o1, Account o2) {
306                         return o1.name.compareTo(o2.name);
307                 }
308         }
309         
310         private class SortById implements Comparator<Account>
311         {
312                 @Override
313                 public int compare(Account o1, Account o2) {
314                         int a = Integer.valueOf(o1.num, 16);
315                         int b = Integer.valueOf(o2.num, 16);
316                         return( a - b );
317                 }
318         }
319         
320         
321         
322         public void listAddAcc(String num, String name)
323         {
324                 list.addElement( new Account(num,name) );
325                 
326                 Collections.sort(list, sortMethod);
327         }
328         
329         public void listClear()
330         {
331                 list.clear();
332         }
333         
334         //Return the full list
335         public Vector<Account> getList()
336         {
337                 return(list);
338         }
339         
340         //Return a vector of accounts matching the keyword
341         public Vector<Account> getList(String key)
342         {
343                 Vector<Account> res = new Vector<Account>();
344                 String k = key.toLowerCase();
345                 for( Account a : list )
346                 {
347                         if( a.name.toLowerCase().contains( k ) ) 
348                         {
349                                 res.add(a);
350                         }
351                 }
352                 
353                 return(res);
354         }
355
356         public String[] getAvailableLayouts()
357         {
358                 String[] layouts = supportedLayouts.toArray(new String[supportedLayouts.size()]);
359                 return(layouts);
360         }
361         
362         public String addAvailableLayout( String str )
363         {
364                 //available formats are in order and of the form: '  N. Layout' so we cut
365                 //space space N dot and space.
366                 String l = str.substring(5);
367                 supportedLayouts.add(l);
368                 return(l);
369         }
370         
371
372
373         @Override
374         public void actionPerformed(ActionEvent e) {
375                 Account f=null;
376                 for( Account a : list )
377                 {
378                         if( a.num.compareTo( e.getActionCommand().substring(1, 3)) == 0 )
379                         {
380                                 f=a;
381                                 break;
382                         }
383                 }
384                 if(f!=null)
385                 {
386                         trig(f, e.getActionCommand().charAt(0), null);
387                 }
388         }
389
390         public void setCurrentLayout(String str) {
391                 System.out.println("FkManager: Got current layout:" + str );
392                 keyLayout =str;
393         }
394
395         public String getCurrentLayout()
396         {
397                 return( keyLayout );
398         }
399
400         private class NewAccountTask implements Runnable
401         {
402                 private String seq;
403                 private Account acc;
404                 FkActionEventListener delegate;
405                 public NewAccountTask(String sequence, FkActionEventListener d, String accountName)
406                 {
407                         seq=sequence;
408                         delegate=d;
409                         acc = new Account( "00", accountName );
410                         System.out.println("NewAccountTask ctor");
411                 }
412                 @Override
413                 public void run() {
414                         System.out.println("NewAccountTask run");
415                         Boolean noTimeOut = false;
416
417                         //First check that we get a prompt
418                         if( !checkState() )
419                         {
420                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.STATE_ERROR, "EXCEPTION",acc,'A') );
421                                 return;
422                         }
423                         
424                         String data="";
425                         int timeOut;
426                         try {                   
427                         //First type x a and wait for account title
428                                 
429                                 com.serialPort.writeByte((byte)'x');
430                                 com.serialPort.writeByte((byte)'a');
431                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_WAITING, "WAITING",acc,'A') );
432
433                                 timeOut = 6000;
434                                 while(timeOut > 0)
435                                 {
436                                         if( com.serialPort.getInputBufferBytesCount() > 0 )
437                                         {
438                                                 String in = com.serialPort.readString(); 
439                                                 data += in;
440                                                 System.out.println("Datain:" +in);
441                                                 if( data.contains("Account Title, (0-31):") )
442                                                 {
443                                                         System.out.println("Found");
444                                                         break;
445                                                 }
446                                         } else {
447                                                 timeOut -= 50;
448                                                 Thread.sleep(50);
449                                         }
450                                 }
451                                 
452                                 if(timeOut > 0 )
453                                 {
454                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_WORKING, "WORKING",acc,'A') );
455
456                                         for(int p=0; p < seq.length();p++)
457                                         {
458
459                                                 if( com.serialPort.getOutputBufferBytesCount() > 1 )
460                                                 {
461                                                         Thread.sleep(100);
462                                                         System.out.println("[SLEEP]");
463                                                 } else {
464                                                         if( seq.charAt(p) == PAUSE_CODE)
465                                                         {
466                                                                 Thread.sleep(100);
467                                                                 System.out.println("[DELAY]");
468                                                         } else {
469                                                                 if( seq.charAt(p) == ENTER_KEY )
470                                                                 {
471                                                                         System.out.println( "[ENTER]" );
472                                                                 } else {
473                                                                         System.out.println( "Type from pos ("+p+"):" + seq.charAt(p));
474                                                                 }
475         
476                                                                 com.serialPort.writeByte( (byte)seq.charAt(p) );
477                                                                 Thread.sleep(5);
478                                                         }
479                                                         
480                                                 }
481                                                 
482                                                 if(  com.serialPort.getInputBufferBytesCount() > 0 )
483                                                 {
484                                                         System.out.print("Reading "+com.serialPort.getInputBufferBytesCount()+ " bytes: ");
485                                                         data="";
486                                                         while( com.serialPort.getInputBufferBytesCount() > 0 )
487                                                         {
488                                                                 String in = com.serialPort.readString();
489                                                                 data += in;
490                                                                 
491                                                         }
492                                                         if( data.contains("[generate]") )
493                                                         {
494                                                                 noTimeOut=true;
495                                                                 System.out.println("1: noTimeOut");
496                                                         }                                                       
497                                                         System.out.println(data);
498                                                 }
499                                         }//While typing
500                                         System.out.println("All chars typed, waiting for [save entry ");
501                                         
502                                         int step=0;
503                                         
504                                         timeOut = 30000;
505                                         while( timeOut > 0 || noTimeOut )
506                                         {
507                                                 
508                                                 if( com.serialPort.getInputBufferBytesCount() > 0 )
509                                                 {
510                                                         String in = com.serialPort.readString();
511                                                         //System.out.println("Read>"+in);
512                                                         data += in;
513                                                         System.out.println("Data:"+data);
514                                                         
515                                                         //Check for abort first, in that case, we don't want to do anything.
516                                                         if( data.contains("[abort]") )
517                                                         {
518                                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ABORTED, "ERROR:"+data,null,'A') );
519                                                                 return;
520                                                         }
521                                                         
522                                                         //If we're generating a password, we don't want to timeout
523                                                         if( data.contains("[generate]") )
524                                                         {
525                                                                 noTimeOut=true;
526                                                                 System.out.println("2: noTimeOut");
527                                                                 
528                                                         }
529                                                         
530                                                         //If we're at step0 and have the saveentry string and there's a ] after it, we're good to fetch the account number.
531                                                         if( step==0 && data.contains("[save entry ") && (data.lastIndexOf(']') > data.lastIndexOf("[save entry ")) )
532                                                         {
533                                                                 int begin = data.lastIndexOf("[save entry ")+12;
534                                                                 String subStr = data.substring(begin);
535                                                                 int end = subStr.indexOf(']');
536                                                                 subStr = subStr.substring(0,end);
537                                                                 if( subStr.length()==1)
538                                                                 {
539                                                                         subStr = "0"+subStr;
540                                                                 }
541                                                                 acc.num = subStr;
542                                                                 listAddAcc( acc.num, acc.name);
543                                                                 System.out.println("Account: "+acc.num+" " + acc.name);
544                                                                 
545                                                                 //Cut data, so that the [done] step1 looks for will not be the one after [generate].
546                                                                 data = data.substring(begin+end);
547                                                                 
548                                                                 System.out.println("Found [save entry, looking for done.");
549                                                                 step++;
550                                                         }
551                                                         
552                                                         if( step==1 && data.contains("[done]") )
553                                                         {
554                                                                 System.out.println("Found [done] in: {"+data+"}");
555                                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_OKAY, "DONE:"+data,acc,'A') );
556                                                                 return;
557                                                         }
558
559                                                 } else {
560                                                         Thread.sleep(50);
561                                                         timeOut -= 50;
562                                                 }
563                                         } //While !timeout 
564                                 } //If !timeout
565                                 
566                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ABORTED, "TIMEOUT:"+data,null,'A') );
567
568                         } catch (Exception e) {
569                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ERROR, "EXCEPTION",null,'A') );
570                         }
571                         
572                 }
573
574                 
575         }
576
577         private void flushSerial() {
578                 try {
579                         Thread.sleep(10);
580                         com.serialPort.purgePort(SerialPort.PURGE_RXCLEAR | SerialPort.PURGE_TXCLEAR);
581                         
582                         System.out.println("Flushed "+com.serialPort.getInputBufferBytesCount()+" bytes.");
583                         while( com.serialPort.getInputBufferBytesCount() > 0 )
584                         {
585                                 com.serialPort.readBytes();
586                         }
587                         
588                 } catch(Exception e)
589                 {
590                         System.out.println("FkManager flushSerial Exception:"+e.getMessage() );
591                 }
592                 
593         }       
594         
595         public void createAccount(String strAccountName, String strUserName,
596                         Boolean autoPassword, int autoPassLen, Boolean autoPassAllSpecials,
597                         String autoPassSpecials, String strPassword, Boolean seperatorTab,
598                         FkActionEventListener delegate) {
599                         
600                         StringBuilder sb = new StringBuilder(256);
601                 
602                         
603                         
604                         sb.append(strAccountName);
605                         sb.append(ENTER_KEY);
606                         sb.append(PAUSE_CODE);
607                         sb.append(strUserName);
608                         sb.append(ENTER_KEY);
609                         sb.append(PAUSE_CODE);
610                         
611                         if( autoPassword )
612                         {
613                                 sb.append('2'); //Select automatic password
614                                 sb.append(PAUSE_CODE);
615                                 sb.append(autoPassLen);
616                                 sb.append(ENTER_KEY);
617
618                                 
619                                 if( autoPassAllSpecials )
620                                 {
621                                         sb.append('1'); //Allow all characters
622                                 } else {
623                                         if( autoPassSpecials.length() > 0 )
624                                         {
625                                                 sb.append('2'); //Allow only characters specified below
626                                                 sb.append(PAUSE_CODE);
627                                                 sb.append(autoPassSpecials);
628                                                 sb.append(ENTER_KEY);
629                                         } else {
630                                                 sb.append('3'); // Allow no special characters
631                                         }
632                                 }
633                         } else {
634                                 //Manual entered password
635                                 sb.append('1'); // Select manual password
636                                 sb.append(PAUSE_CODE);
637                                 sb.append(strPassword);
638                                 sb.append(ENTER_KEY);
639                         }
640                         
641                         sb.append(PAUSE_CODE);
642
643                         //Tab/Enter sep
644                         if( seperatorTab )
645                         {
646                                 sb.append('1'); //Select tab seperator
647                         } else {
648                                 sb.append('2'); //Select enter seperator
649                         }
650                         
651                         String seq = sb.toString();
652                         
653                         System.out.println("Seq ["+seq.replace(PAUSE_CODE, 'ø').replace(ENTER_KEY, 'å')+"]");
654                         
655                         NewAccountTask newTask = new NewAccountTask(seq, delegate, strAccountName);
656                         new Thread(newTask).start();                    
657                 
658         }
659
660         public void sortById(boolean byId) {
661                 if( byId )
662                 {
663                         sortMethod = new SortById();
664                 } else {
665                         sortMethod = new SortByName();
666                 }
667                 if(list != null )
668                 {
669                         Collections.sort(list, sortMethod);
670                 }
671         }
672
673         public void setBanner(String _banner) {
674                 banner = _banner;
675                 
676         }
677         
678         public String getBanner()
679         {
680                 return(banner);
681         }
682         
683         public boolean isStringValidForFk(String str)
684         {
685
686                 char passChars[] = {
687                                 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E',
688                                 'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T',
689                                 'U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i',
690                                 'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x',
691                                 'y','z','!','"','#','$','%','&','@','?','(',')','[',']','-',
692                                 '.',',','+','{','}','_','/','<','>','=','|','\'','\\', 
693                                 ';',':',' ','*'// <- 92 (idx 91)
694                                 };
695
696
697                 int len = str.length();
698                 for(int i=0; i<len; i++)
699                 {
700                         boolean valid=false;
701
702                         for(char c : passChars)
703                         {
704                                 if(str.charAt(i)==c)
705                                 {
706                                         valid=true;
707                                 }
708                         }
709
710                         if(!valid)
711                         {
712                                 System.out.println("FkManager.isStringValidForFk(); Invalid character '"+str.charAt(i)+"' at pos "+i+" in '"+str+"'");
713                                 return false;
714                         }
715                 }
716
717                 return true;
718         }
719
720         
721         
722         ///
723         private class BannerTask implements Runnable
724         {
725
726                 private FkActionEventListener delegate;
727                 private String bannerTxt;
728                 
729                 public BannerTask(String txt, FkActionEventListener d )
730                 {
731                         bannerTxt=txt;
732                         delegate=d;
733                 }
734                 
735                 @Override
736                 public void run() {
737                         
738                         int timeOut=0;
739                         String data="";
740
741
742                         try {
743                         
744                                 //First check that we get a prompt
745                                 if( !checkState() )
746                                 {
747                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.STATE_ERROR, "EXCEPTION",null,'b') );
748                                         return;
749                                 }
750
751                                 //Drain any previous input.
752                                 while( com.serialPort.getInputBufferBytesCount() > 0 )
753                                 {
754                                         com.serialPort.readBytes();
755                                 }
756                                 
757                                 com.serialPort.writeByte((byte)'x');
758                                 com.serialPort.writeByte((byte)'b');
759                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_WAITING, "WAITING",null,'b') );
760
761                                 timeOut = 6000;
762                                 while(timeOut > 0)
763                                 {
764                                         if( com.serialPort.getInputBufferBytesCount() > 0 )
765                                         {
766                                                 String in = com.serialPort.readString(); 
767                                                 data += in;
768                                                 System.out.println("Datain:" +in);
769                                                 if( data.contains("Banner (0-31):\r\n") )
770                                                 {
771                                                         System.out.println("Found");
772                                                         break;
773                                                 }
774                                         } else {
775                                                 timeOut -= 50;
776                                                 Thread.sleep(50);
777                                         }
778                                 }
779                                 
780
781                                 if(timeOut > 0 )
782                                 {
783                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_WORKING, "WORKING",null,'b') );
784
785                                         for(int p=0; p < bannerTxt.length();p++)
786                                         {
787                                                 com.serialPort.writeByte( (byte)bannerTxt.charAt(p) );
788                                                 Thread.sleep(5);
789                                         }
790
791                                         while( com.serialPort.getInputBufferBytesCount() > 0 )
792                                         {
793                                                 com.serialPort.readBytes();
794                                         }
795
796                                         com.serialPort.writeByte( (byte)ENTER_KEY );
797
798                                         timeOut = 3000;
799                                         while(timeOut > 0)
800                                         {
801                                                 if( com.serialPort.getInputBufferBytesCount() > 0 )
802                                                 {
803                                                         String in = com.serialPort.readString(); 
804                                                         data += in;
805                                                         System.out.println("Datain:" +in);
806                                                         if( data.contains("[done]\r\n") )
807                                                         {
808                                                                 System.out.println("Found");
809                                                                 break;
810                                                         }
811                                                 } else {
812                                                         timeOut -= 50;
813                                                         Thread.sleep(50);
814                                                 }
815                                         }
816                                 }
817
818                                 if( timeOut > 0 )
819                                 {
820
821                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_OKAY, "Saved:"+data,null,'b') );
822                                 } else {
823                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ABORTED, "TIMEOUT:"+data,null,'b') );
824                                 }
825
826                         } catch(Exception e)
827                         {
828                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ERROR, "EXCEPTION",null,'b') );
829                                 return;
830                         }
831                 }
832
833         }
834
835         public void saveBanner(String bannerTxt, FkActionEventListener eventListener) {
836                 BannerTask bannerTask = new BannerTask(bannerTxt, eventListener);
837                 new Thread(bannerTask).start();
838         }
839
840         
841         private class LayoutTask implements Runnable
842         {
843
844                 private FkActionEventListener delegate;
845                 private String layout;
846                 
847                 public LayoutTask(int _Layout, FkActionEventListener d )
848                 {
849                         layout = ""+_Layout;
850                         delegate=d;
851                 }
852                 
853                 @Override
854                 public void run() {
855                         
856                         int timeOut=0;
857                         String data="";
858
859
860                         try {
861                         
862                                 //First check that we get a prompt
863                                 if( !checkState() )
864                                 {
865                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.STATE_ERROR, "EXCEPTION",null,'k') );
866                                         return;
867                                 }
868
869                                 //Drain any previous input.
870                                 while( com.serialPort.getInputBufferBytesCount() > 0 )
871                                 {
872                                         com.serialPort.readBytes();
873                                 }
874                                 
875                                 com.serialPort.writeByte((byte)'x');
876                                 com.serialPort.writeByte((byte)'k');
877                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_WAITING, "WAITING",null,'k') );
878
879                                 timeOut = 6000;
880                                 while(timeOut > 0)
881                                 {
882                                         if( com.serialPort.getInputBufferBytesCount() > 0 )
883                                         {
884                                                 String in = com.serialPort.readString(); 
885                                                 data += in;
886                                                 System.out.println("Datain:" +in);
887                                                 if( data.contains("Select keyboard layout:\r\n") && data.contains("\r\n% ") )
888                                                 {
889                                                         System.out.println("Found");
890                                                         break;
891                                                 }
892                                         } else {
893                                                 timeOut -= 50;
894                                                 Thread.sleep(50);
895                                         }
896                                 }
897
898                                 if(timeOut > 0 )
899                                 {
900
901
902                                         com.serialPort.writeByte( (byte)layout.charAt(0) );
903
904
905                                         while( com.serialPort.getInputBufferBytesCount() > 0 )
906                                         {
907                                                 com.serialPort.readBytes();
908                                         }
909
910                                         com.serialPort.writeByte( (byte)ENTER_KEY );
911
912                                         timeOut = 3000;
913                                         while(timeOut > 0)
914                                         {
915                                                 if( com.serialPort.getInputBufferBytesCount() > 0 )
916                                                 {
917                                                         String in = com.serialPort.readString(); 
918                                                         data += in;
919                                                         System.out.println("Datain:" +in);
920                                                         if( data.contains("test.\r\n#") )
921                                                         {
922                                                                 System.out.println("Found");
923                                                                 break;
924                                                         }
925                                                 } else {
926                                                         timeOut -= 50;
927                                                         Thread.sleep(50);
928                                                 }
929                                         }
930                                 }
931
932                                 timeOut = 30000;
933                                 while(timeOut > 0)
934                                 {
935                                         if( com.serialPort.getInputBufferBytesCount() > 0 )
936                                         {
937                                                 String in = com.serialPort.readString(); 
938                                                 data += in;
939                                                 System.out.println("Datain:" +in);
940                                                 if( data.contains("Correct [y/n] ?") )
941                                                 {
942                                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_WORKING, "WORKING",null,'k') );
943                                                         System.out.println("Found");
944                                                         com.serialPort.writeByte((byte)'y');
945
946                                                         Thread.sleep(500); //sleep before returning the result, give the UI plenty of time to catch up.
947
948                                                         break;
949                                                 }
950                                         } else {
951                                                 timeOut -= 50;
952                                                 Thread.sleep(50);
953                                         }
954                                 }                               
955
956                                 if( timeOut > 0 )
957                                 {
958                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_OKAY, "Saved:"+data,null,'k') );
959                                 } else {
960                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ABORTED, "TIMEOUT:"+data,null,'k') );
961                                 }
962
963                         } catch(Exception e)
964                         {
965                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ERROR, "EXCEPTION",null,'k') );
966                                 return;
967                         }
968                 }
969
970         }
971         
972         public void saveLayout(int layout, FkActionEventListener eventListener) {
973                 LayoutTask lt = new LayoutTask(layout, eventListener);
974                 new Thread(lt).start();
975         }
976         
977         
978         private class BackupTask implements Runnable
979         {
980
981                 private FkActionEventListener delegate;
982                 private File file;
983                 
984                 
985                 public BackupTask(File f, FkActionEventListener d )
986                 {
987                         file=f;
988                         delegate=d;
989                 }
990                 
991                 
992                 byte crc8( byte[] dat, int begin, int len )
993                 {
994                         int crc = 0;
995                         
996                         int idx=0;
997
998                         while( len-- != 0 )
999                         {
1000                                 int inbyte = dat[begin+idx];
1001                                 idx++;
1002                                 
1003                                 for(int i=8; i!=0; i--)
1004                                 {
1005                                         int mix =  (( crc ^ inbyte) & 0x01);
1006                                         crc >>=1;
1007                                         if( mix != 0 )
1008                                         {
1009                                                 crc ^=0x8C;
1010                                         }
1011                                         inbyte >>=1;
1012                                 }
1013                         }
1014                         return((byte)crc);
1015                 }
1016                 
1017                 @Override
1018                 public void run() {
1019                         
1020                         int timeOut=0;
1021                         String data="";
1022
1023                         int state=0;
1024
1025                         try {
1026
1027                                 //First check that we get a prompt
1028                                 if( !checkState() )
1029                                 {
1030                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.STATE_ERROR, "EXCEPTION",null,'e') );
1031                                         return;
1032                                 }
1033
1034                                 //Drain any previous input.
1035                                 while( com.serialPort.getInputBufferBytesCount() > 0 )
1036                                 {
1037                                         com.serialPort.readBytes();
1038                                 }
1039                                 com.serialPort.writeByte((byte)'X');
1040                                 com.serialPort.writeByte((byte)'e');
1041
1042                                 
1043                                 timeOut = 6000;
1044                                 
1045                                 ByteBuffer bin = new ByteBuffer( 67000 );
1046
1047                                 while(timeOut > 0)
1048                                 {
1049                                         if( com.serialPort.getInputBufferBytesCount() > 0 )
1050                                         {
1051                                                 
1052                                                 int n = com.serialPort.getInputBufferBytesCount();
1053                                                 while(n-->0)
1054                                                 {
1055                                                         bin.append( com.serialPort.readBytes(1)[0]);
1056                                                 }
1057
1058
1059                                                 data = new String(bin.toArray());
1060
1061                                                 if( state==0 && data.contains("[RDY]\r\n") )
1062                                                 {
1063                                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_WAITING, "WAITING",null,'e') );
1064
1065                                                         state=1;
1066                                                 }
1067
1068                                                 if( state==1 && data.contains("[OK]\r\n[BEGIN]" ) )
1069                                                 {
1070                                                         state=2;
1071
1072                                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_WORKING, "Working",null,'e') );
1073                                                 }
1074
1075                                                 if( state==2 )
1076                                                 {
1077                                                         timeOut=3000;
1078                                                         if( data.contains("[END]") )
1079                                                         {
1080
1081                                                                 //data = data.substring(0, data.length()-5 );
1082                                                                 
1083                                                                 bin.trimToSize();
1084                                                                 
1085                                                                 byte[] arr = bin.toArray();
1086                                                                 
1087                                                                 byte[] clean = new byte[66000];
1088                                                                 
1089                                                                 state=0;
1090                                                                 int idx=0;
1091                                                                 int cc=0;
1092                                                                 String errors="";
1093                                                                 for(int i=0;i< arr.length; i++ )
1094                                                                 {
1095                                                                         //Find [BEGIN]
1096                                                                         if(state==0 && i+6 < arr.length)
1097                                                                         {
1098                                                                                 if(arr[i] == '[' &&arr[i+1] == 'B' &&arr[i+2] == 'E' &&arr[i+3] == 'G' &&arr[i+4] == 'I' &&arr[i+5] == 'N' &&arr[i+6] == ']' )
1099                                                                                 {
1100                                                                                         i+=7;
1101                                                                                         state=1;
1102                                                                                         idx=0;
1103                                                                                 }
1104                                                                         }
1105                                                                         
1106                                                                         if(state==1)
1107                                                                         {
1108                                                                                 
1109                                                                                 if( idx < 66000 )
1110                                                                                 {
1111                                                                                         clean[idx] = arr[i];
1112                                                                                         cc++;
1113                                                                                         
1114                                                                                         if( cc==33  )
1115                                                                                         {
1116                                                                                                 cc=0;
1117
1118                                                                                                 byte c = crc8(clean,(idx-32),32);
1119                                                                                                 
1120                                                                                                 if( c != clean[idx] )
1121                                                                                                 {
1122                                                                                                         errors += "CRC Error: Bytes "+(idx-32)+" to "+(idx-1)+" have CRC "+c+" but the CRC sent from FinalKey in byte "+idx+" is "+clean[idx]+"\n";
1123                                                                                                 }
1124                                                                                         }
1125                                                                                         
1126                                                                                         idx++;
1127                                                                                 } else {
1128                                                                                         if( arr[i] == '['  &&arr[i+1] == 'E' &&arr[i+2] == 'N' &&arr[i+3] == 'D' &&arr[i+4] == ']' )
1129                                                                                         {
1130                                                                                                 FileOutputStream fout = new FileOutputStream( file );
1131                                                                                                 fout.write( clean );
1132                                                                                                 fout.close();
1133                                                                                                 
1134                                                                                                 if( errors.length() == 0 )
1135                                                                                                 {
1136                                                                                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_OKAY, "Done" ,null,'e') );
1137                                                                                                 } else {
1138                                                                                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ERROR, errors ,null,'e') );
1139                                                                                                 }
1140                                                                                                 return;
1141                                                                                         } else {
1142                                                                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.UNEXPECTED_ACTION_RESULT_ERROR, "Read past the end?" ,null,'e') );
1143                                                                                         }
1144                                                                                 }
1145                                                                         }
1146
1147                                                                 }
1148
1149                                                                 break;
1150                                                         } else {
1151                                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.PROGRESS_UPDATE, ""+bin.size() ,null,'e') );
1152                                                         }
1153                                                 }
1154
1155                                         } else {
1156                                                 timeOut -= 50;
1157                                                 Thread.sleep(50);
1158                                         }
1159                                 }
1160                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ABORTED, "Timed out" ,null,'e') );
1161
1162                         } catch (Exception e) { 
1163
1164                         }
1165                 }
1166         }
1167
1168         public void backup(File f, FkActionEventListener delegate) {
1169                 BackupTask bt = new BackupTask(f, delegate );
1170                 new Thread(bt).start();
1171         }
1172
1173         private class RestoreTask implements Runnable
1174         {
1175
1176                 private FkActionEventListener delegate;
1177                 private File file;
1178
1179                 public RestoreTask(File f, FkActionEventListener d )
1180                 {
1181                         file=f;
1182                         delegate=d;
1183                 }
1184
1185
1186                 @Override
1187                 public void run() {
1188
1189                         int state=0;
1190                         long timeStamp=System.currentTimeMillis();
1191
1192                         String data="";
1193
1194                         byte[] bin = new byte[66000];
1195
1196                         boolean fileOk=false;
1197                         FileInputStream in=null;
1198                         try {
1199                                 in = new FileInputStream(file);
1200
1201                                 int len = in.read(bin);
1202                                 if( len == 66000 )
1203                                 {
1204                                         fileOk=true;
1205                                 }
1206
1207                         } catch (Exception e1) {
1208                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.FILE_ERROR, e1.getLocalizedMessage() ,null,'i') );
1209                                 return;
1210                         } finally {
1211                                 try {
1212                                         if( in != null )
1213                                         {
1214                                                 in.close();
1215                                         }
1216                                 } catch (IOException e) {}
1217                         }
1218
1219                         String verify = new String( bin ).substring(0,9);
1220
1221                         if( !fileOk  || verify.compareTo("[FinalKey") != 0)
1222                         {
1223                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.FILE_ERROR, "Not a FinalKey backup file." ,null,'i') );
1224                                 fileOk=false;
1225                         }
1226
1227                         if( !fileOk )
1228                         {
1229                                 return;
1230                         }
1231
1232                         try {
1233
1234                                 //First check that we get a prompt
1235                                 if( !checkState() )
1236                                 {
1237                                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.STATE_ERROR, "EXCEPTION",null,'i') );
1238                                         return;
1239                                 }
1240
1241                                 //Drain any previous input.
1242                                 while( com.serialPort.getInputBufferBytesCount() > 0 )
1243                                 {
1244                                         com.serialPort.readBytes();
1245                                 }
1246                                 com.serialPort.writeByte((byte)'X');
1247                                 com.serialPort.writeByte((byte)'i');
1248
1249
1250                                 int idx=0;
1251                                 while(System.currentTimeMillis() - timeStamp < 6000L)
1252                                 {
1253                                         if( com.serialPort.getInputBufferBytesCount() > 0 )
1254                                         {
1255                                                 data += com.serialPort.readString();
1256                                         }
1257                                         if( state==0 && data.contains("[RDY]\r\n") )
1258                                         {
1259                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_WAITING, "WAITING",null,'i') );
1260                                                 data="";
1261                                                 state=1;
1262                                         }
1263
1264                                         if( state==1 && data.contains("[NO]") )
1265                                         {
1266                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ABORTED, "",null,'i') );
1267                                                 return;
1268                                         }
1269
1270                                         if( state==1 && data.contains("[OK]\r\n") )
1271                                         {
1272                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_WORKING, "WORKING",null,'i') );
1273                                                 state=2;
1274
1275                                                 //Don't reset data, state2 may need some of it
1276                                         }
1277
1278                                         //Catch E = END, F=Fail, C=CRC Fail O=CRC OK
1279                                         if( data.contains("E") )
1280                                         {
1281                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_OKAY, "Done.",null,'i') );
1282                                                 return;
1283                                         }
1284
1285                                         if( data.contains("F") )
1286                                         {
1287                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ERROR, "Device reported read-error.",null,'i') );
1288                                                 return;
1289                                         }
1290
1291                                         if( data.contains("C") )
1292                                         {
1293                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ERROR, "Device reported CRC-error.",null,'i') );
1294                                                 return;
1295                                         }
1296
1297                                         //Device ready for more data
1298                                         if( state==2 && data.contains("R") )
1299                                         {
1300                                                 data="";
1301                                                 timeStamp = System.currentTimeMillis();
1302
1303                                                 byte[] outBuf = new byte[33];
1304
1305                                                 for(int i=0;i<33;i++)
1306                                                 {
1307                                                         outBuf[i] = bin[idx++];
1308                                                 }
1309
1310                                                 com.serialPort.writeBytes(outBuf);
1311
1312                                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.PROGRESS_UPDATE, ""+idx ,null,'i') );
1313
1314                                                 state=3;
1315                                         }
1316
1317                                         if( state == 3 && data.contains("W") && data.contains("O") )
1318                                         {
1319                                                 state=2;
1320                                         }
1321
1322                                 }
1323                         } catch(Exception e)
1324                         {
1325                                 Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ERROR, "Exception: "+e.getLocalizedMessage(),null,'i') );
1326                                 return;
1327
1328                         }
1329
1330                         Display.getDefault().asyncExec( new FkActionEventMsg(delegate, FkActionEventListener.FkActionEventType.ACTION_ERROR, "No data from device.",null,'i') );
1331
1332                 }
1333         }
1334
1335         public void restore(File f, FkActionEventListener delegate) {
1336                 RestoreTask rt = new RestoreTask(f, delegate );
1337                 new Thread(rt).start();
1338         }
1339
1340         public void disconnect() {
1341                 com.disconnect();
1342         }
1343
1344 }