1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 package com.levelonelabs.aim;
34
35 import java.io.BufferedOutputStream;
36 import java.io.BufferedReader;
37 import java.io.DataInputStream;
38 import java.io.DataOutputStream;
39 import java.io.IOException;
40 import java.io.InterruptedIOException;
41 import java.io.StringReader;
42 import java.net.InetAddress;
43 import java.net.Socket;
44 import java.net.UnknownHostException;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Date;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.List;
52 import java.util.Map;
53 import java.util.Set;
54 import java.util.StringTokenizer;
55 import java.util.Timer;
56 import java.util.TimerTask;
57 import java.util.logging.Logger;
58
59
60 /***
61 * Implements the AIM protocol
62 *
63 * @author Scott Oster, Will Gorman
64 * @created September 4, 2001
65 */
66 public class AIMClient implements Runnable, AIMSender {
67
68 private static final long TIME_DELAY = 5 * 60 * 1000;
69 private static final String PING = "PING";
70
71 private AimConnectionCheck watchdogCheck;
72 private AimConnectionCheck watchdogVerify;
73 boolean connectionVerified = false;
74 private Timer connectionCheck = new Timer();
75
76 static Logger logger = Logger.getLogger(AIMClient.class.getName());
77
78
79 private static final int MAX_POINTS = 10;
80
81 private static final int RECOVER_RATE = 2200;
82
83
84
85
86 private static final String REVISION = "\"TIC:TOC2\" 160";
87
88 private String loginServer = "toc.oscar.aol.com";
89
90 private int loginPort = 5190;
91
92 private String authorizerServer = "login.oscar.aol.com";
93
94 private int authorizerPort = 29999;
95
96 private List aimListeners = new ArrayList();
97
98 String name;
99
100 private String pass;
101
102 private String info;
103
104 private String nonUserResponse;
105
106 boolean online;
107
108 private boolean autoAddUsers = false;
109
110
111 private int seqNo;
112
113 private Socket connection;
114
115 private DataInputStream in;
116
117 private DataOutputStream out;
118
119 private Map buddyHash;
120
121 private int sendLimit = MAX_POINTS;
122
123 private long lastFrameSendTime = System.currentTimeMillis();
124
125 private int permitMode = PERMIT_ALL;
126
127 private Set permitted;
128
129 private Set denied;
130
131
132 /***
133 * Constructor for the AIMClient object
134 *
135 * @param name
136 * @param pass
137 * @param info
138 * Description of the Parameter
139 * @param response
140 * what to say to non-users when they message the bot (if
141 * autoaddUsers==false)
142 * @param autoAddUsers
143 */
144 public AIMClient(String name, String pass, String info, String response, boolean autoAddUsers) {
145 this.nonUserResponse = response;
146
147 buddyHash = new HashMap();
148 permitted = new HashSet();
149 denied = new HashSet();
150 this.name = imNormalize(name);
151 this.pass = pass;
152 this.info = info;
153 this.autoAddUsers = autoAddUsers;
154 this.addBuddy(new AIMBuddy(name));
155 }
156
157
158 /***
159 * Constructor for the AIMClient object
160 *
161 * @param name
162 * @param pass
163 * @param info
164 * Description of the Parameter
165 * @param autoAddUsers
166 */
167 public AIMClient(String name, String pass, String info, boolean autoAddUsers) {
168 this(name, pass, info, "Sorry, you must be a user of this system to send requests.", autoAddUsers);
169 }
170
171
172 /***
173 * Constructor for the AIMClient object
174 *
175 * @param name
176 * @param pass
177 * @param info
178 * Description of the Parameter
179 */
180 public AIMClient(String name, String pass, String info) {
181 this(name, pass, info, false);
182 }
183
184
185 /***
186 * Constructor for the AIMClient object
187 *
188 * @param name
189 * @param pass
190 */
191 public AIMClient(String name, String pass) {
192 this(name, pass, "No info", false);
193 }
194
195
196 /***
197 * Strip out HTML from a string
198 *
199 * @param line * *
200 * @return the string without HTML
201 */
202 public static String stripHTML(String line) {
203 StringBuffer sb = new StringBuffer(line);
204 String out = "";
205
206 for (int i = 0; i < (sb.length() - 1); i++) {
207 if (sb.charAt(i) == '<') {
208
209 if ((sb.charAt(i + 1) == '/') || ((sb.charAt(i + 1) >= 'a') && (sb.charAt(i + 1) <= 'z'))
210 || ((sb.charAt(i + 1) >= 'A') && (sb.charAt(i + 1) <= 'Z'))) {
211 for (int j = i + 1; j < sb.length(); j++) {
212 if (sb.charAt(j) == '>') {
213 sb = sb.replace(i, j + 1, "");
214 i--;
215 break;
216 }
217 }
218 } else if (sb.charAt(i + 1) == '!') {
219
220 for (int j = i + 1; j < sb.length(); j++) {
221 if ((sb.charAt(j) == '>') && (sb.charAt(j - 1) == '-') && (sb.charAt(j - 2) == '-')) {
222 sb = sb.replace(i, j + 1, "");
223 i--;
224 break;
225 }
226 }
227 }
228 }
229 }
230
231 out = sb.toString();
232 return out;
233 }
234
235
236 /***
237 * Protocol method
238 *
239 * @para * *
240 * @return roasted string
241 */
242 private static String imRoast(String pass) {
243 String roast = "Tic/Toc";
244 String out = "";
245 String in = pass;
246 String out2 = "0x";
247 for (int i = 0; i < in.length(); i++) {
248 out = java.lang.Long.toHexString(in.charAt(i) ^ roast.charAt(i % 7));
249 if (out.length() < 2) {
250 out2 = out2 + "0";
251 }
252
253 out2 = out2 + out;
254 }
255
256 return out2;
257 }
258
259
260 /***
261 * Protocol method * in
262 *
263 * @return normalized string
264 */
265 private static String imNormalize(String in) {
266 String out = "";
267 in = in.toLowerCase();
268 char[] arr = in.toCharArray();
269 for (int i = 0; i < arr.length; i++) {
270 if (arr[i] != ' ') {
271 out = out + "" + arr[i];
272 }
273 }
274
275 return out;
276 }
277
278
279 /***
280 * Retrieve a buddy from the list
281 *
282 * @param buddyName
283 * @return The buddy
284 */
285 public AIMBuddy getBuddy(String buddyName) {
286 return (AIMBuddy) buddyHash.get(imNormalize(buddyName));
287 }
288
289
290 /***
291 * Get an iterator for all the current buddy names
292 *
293 * @return iterator
294 */
295 public Iterator getBuddyNames() {
296 return Arrays.asList(buddyHash.keySet().toArray()).iterator();
297 }
298
299
300 /***
301 * Sign on to aim server
302 */
303 public void signOn() {
304 new Thread(this).start();
305
306
307 watchdogCheck = new AimConnectionCheck(this, true);
308
309
310 watchdogVerify = new AimConnectionCheck(this, false);
311 connectionCheck.scheduleAtFixedRate(watchdogCheck, TIME_DELAY, TIME_DELAY);
312 connectionCheck.scheduleAtFixedRate(watchdogVerify, TIME_DELAY + 5000, TIME_DELAY);
313
314
315
316
317 for (int i = 0; i < 10; i++) {
318 if (!this.online) {
319 try {
320 Thread.sleep(2000);
321 } catch (InterruptedException e) {
322 e.printStackTrace();
323 }
324 } else {
325 return;
326 }
327 }
328 }
329
330
331 /***
332 * Sign off from aim server
333 */
334 public void signOff() {
335
336 watchdogCheck.cancel();
337 watchdogVerify.cancel();
338 signoff("User request");
339 }
340
341
342 /***
343 * Main processing method for the AIMClient object
344 */
345 public void run() {
346 int length;
347 seqNo = (int) Math.floor(Math.random() * 65535.0);
348
349
350
351 InetAddress[] loginIPs = null;
352 try {
353 loginIPs = InetAddress.getAllByName(loginServer);
354 } catch (UnknownHostException e) {
355 signoff("0");
356 generateError("Signon err", e.getMessage());
357 return;
358 }
359
360 for (int i = 0; i < loginIPs.length; i++) {
361 try {
362 logger.fine("Attempting to logon using IP:" + loginIPs[i]);
363
364 connection = new Socket(loginIPs[i], loginPort);
365 connection.setSoTimeout(10000);
366 in = new DataInputStream(connection.getInputStream());
367 out = new DataOutputStream(new BufferedOutputStream(connection.getOutputStream()));
368 logger.fine("Successfully connected using IP:" + loginIPs[i]);
369 break;
370 } catch (Exception e) {
371
372 }
373 }
374
375 if (connection == null || in == null || out == null) {
376 signoff("1");
377 generateError("Signon err", "Unable to establish connection to logon server.");
378 return;
379 }
380
381 logger.fine("*** Starting AIM CLIENT (SEQNO:" + seqNo + ") ***");
382 try {
383
384 out.writeBytes("FLAPON\r\n\r\n");
385 out.flush();
386
387 byte[] signon = new byte[10];
388
389 in.readFully(signon);
390
391 out.writeByte(42);
392 out.writeByte(1);
393 out.writeShort(seqNo);
394 seqNo = (seqNo + 1) & 65535;
395 out.writeShort(name.length() + 8);
396
397 out.writeInt(1);
398 out.writeShort(1);
399 out.writeShort(name.length());
400 out.writeBytes(name);
401 out.flush();
402
403
404 frameSend("toc2_signon " + authorizerServer + " " + authorizerPort + " " + name + " " + imRoast(pass)
405 + " English " + REVISION + " " + toc2MagicNumber(name, pass) + "\0");
406
407
408
409 in.skip(4);
410 length = in.readShort();
411 signon = new byte[length];
412 in.readFully(signon);
413 if (new String(signon).startsWith("ERROR")) {
414 fromAIM(signon);
415 logger.severe("Signon error");
416 signoff("2");
417 return;
418 }
419
420 in.skip(4);
421 length = in.readShort();
422 signon = new byte[length];
423 in.readFully(signon);
424
425 frameSend("toc_init_done\0");
426 online = true;
427 generateConnected();
428 frameSend("toc_set_info \"" + info + "\"\0");
429 logger.fine("Done with AIM logon");
430 connection.setSoTimeout(3000);
431 } catch (InterruptedIOException e) {
432 signoff("2.25");
433 } catch (IOException e) {
434 signoff("3");
435 }
436
437 byte[] data;
438 while (true) {
439 try {
440 in.skip(4);
441 length = in.readShort();
442 data = new byte[length];
443 in.readFully(data);
444 fromAIM(data);
445
446 } catch (InterruptedIOException e) {
447
448
449 } catch (IOException e) {
450 logger.severe("*** AIM ERROR: " + e + " ***");
451 break;
452 }
453 }
454 signoff("Connection reset.");
455 }
456
457
458 /***
459 * @param name2
460 * @param pass2
461 * @return
462 */
463 private static int toc2MagicNumber(String username, String password) {
464 int sn = username.charAt(0) - 96;
465 int pw = password.charAt(0) - 96;
466
467 int a = sn * 7696 + 738816;
468 int b = sn * 746512;
469 int c = pw * a;
470
471 return c - a + b + 71665152;
472 }
473
474
475 /***
476 * Register a listener to recieve aim events
477 *
478 * @param listener
479 * The listener
480 */
481 public void addAIMListener(AIMListener listener) {
482 aimListeners.add(listener);
483 }
484
485
486 /***
487 * Send a message to a buddy
488 *
489 * @param buddy
490 * @param text
491 */
492 public void sendMessage(AIMBuddy buddy, String text) {
493 if ((buddy == null) || buddy.isBanned()) {
494 return;
495 }
496
497 if (buddy.isOnline()) {
498 sendMesg(buddy.getName(), text);
499 } else {
500
501
502
503 try {
504 frameSend("toc_get_status " + imNormalize(buddy.getName()) + "\0");
505 } catch (IOException e) {
506 logger.severe("Error sending status request for offline buddy: " + e.getMessage());
507 }
508 }
509 }
510
511
512 /***
513 * Add a single budy
514 *
515 * @param buddy
516 * The buddy to add
517 */
518 public void addBuddy(AIMBuddy buddy) {
519 if (buddy == null) {
520 return;
521 }
522
523 if (getBuddy(buddy.getName()) != null) {
524 return;
525 }
526
527 if (this.online) {
528 String toBeSent = "toc2_new_buddies {g:" + buddy.getGroup() + "\nb:" + imNormalize(buddy.getName()) + "\n}";
529 try {
530 frameSend(toBeSent + "\0");
531 } catch (IOException e) {
532 logger.severe(e.toString());
533 signoff("Error adding buddy");
534 }
535 }
536
537
538 buddyHash.put(imNormalize(buddy.getName()), buddy);
539 }
540
541
542 /***
543 * Convience method for adding multiple buddies
544 *
545 * @param buddyList
546 * List of AIMBuddy
547 */
548 public void addBuddies(List buddyList) {
549
550 Map groupMap = createGroupMap(buddyList);
551
552
553 Iterator groupIter = groupMap.keySet().iterator();
554 while (groupIter.hasNext()) {
555 String group = (String) groupIter.next();
556 String currentlist = "toc2_new_buddies {g:" + group + "\n";
557 List groupList = (List) groupMap.get(group);
558 for (int i = 0; i < groupList.size(); i++) {
559 AIMBuddy buddy = (AIMBuddy) groupList.get(i);
560 buddyHash.put(imNormalize(buddy.getName()), buddy);
561 currentlist += "b:" + imNormalize(buddy.getName()) + "\n";
562 if (currentlist.length() > 1800) {
563 try {
564 frameSend(currentlist + "}\0");
565 currentlist = "toc2_new_buddies {g:" + group + "\n";
566 } catch (IOException e) {
567 e.printStackTrace();
568 logger.severe("ERROR adding buddies.");
569 }
570 }
571 }
572
573 if (currentlist.length() > ("toc2_new_buddies {g:" + group + "\n").length()) {
574 try {
575 frameSend(currentlist + "}\0");
576 } catch (IOException e) {
577 e.printStackTrace();
578 logger.severe("ERROR adding buddies.");
579 }
580 }
581
582 }
583 }
584
585
586 /***
587 * Create a Map of List of buddies in the same group
588 *
589 * @param buddyList
590 * a list of buddies
591 * @return a Map <String, List> keyed with group name with value a list of
592 * buddies in that group
593 */
594 private Map createGroupMap(List buddyList) {
595
596 Map groupMap = new HashMap();
597
598
599 for (Iterator iter = buddyList.iterator(); iter.hasNext();) {
600 Object obj = iter.next();
601 if (obj instanceof AIMBuddy) {
602 AIMBuddy buddy = (AIMBuddy) obj;
603 String group = buddy.getGroup();
604
605 List groupList = (List) groupMap.get(group);
606 if (groupList == null) {
607
608 groupList = new ArrayList();
609 groupMap.put(group, groupList);
610 }
611
612 groupList.add(buddy);
613 }
614 }
615 return groupMap;
616 }
617
618
619 /***
620 * Remove a single budy
621 *
622 * @param buddy
623 * The buddy to add
624 */
625 public void removeBuddy(AIMBuddy buddy) {
626 if (buddy == null) {
627 return;
628 }
629
630 if (getBuddy(buddy.getName()) == null) {
631 return;
632 }
633
634 String buddyname = imNormalize(buddy.getName());
635
636 String toBeSent = "toc2_remove_buddy";
637 try {
638 frameSend(toBeSent + " " + buddyname + " " + buddy.getGroup() + "\0");
639 } catch (IOException e) {
640 logger.severe(e.toString());
641 signoff("Error removing buddy.");
642 }
643
644
645 buddyHash.remove(imNormalize(buddy.getName()));
646 }
647
648
649 /***
650 * Convience method for removing multiple buddies
651 *
652 * @param buddyList
653 * List of AIMBuddy
654 */
655 public void removeBuddies(List buddyList) {
656
657 Map groupMap = createGroupMap(buddyList);
658
659
660 Iterator groupIter = groupMap.keySet().iterator();
661 while (groupIter.hasNext()) {
662 String group = (String) groupIter.next();
663 String currentlist = "toc2_remove_buddy";
664 List groupList = (List) groupMap.get(group);
665 for (int i = 0; i < groupList.size(); i++) {
666 AIMBuddy buddy = (AIMBuddy) groupList.get(i);
667 buddyHash.remove(imNormalize(buddy.getName()));
668 currentlist += " " + imNormalize(buddy.getName());
669 if (currentlist.length() > 1800) {
670 try {
671 frameSend(currentlist + " " + group + "\0");
672 currentlist = "toc2_remove_buddy";
673 } catch (IOException e) {
674 e.printStackTrace();
675 logger.severe("ERROR removing buddies.");
676 }
677 }
678 }
679
680 if (currentlist.length() > "toc2_remove_buddy".length()) {
681 try {
682 frameSend(currentlist + " " + group + "\0");
683 } catch (IOException e) {
684 e.printStackTrace();
685 logger.severe("ERROR adding buddies.");
686 }
687 }
688
689 }
690 }
691
692
693 /***
694 * Warn a buddy
695 *
696 * @param buddy
697 */
698 public void sendWarning(AIMBuddy buddy) {
699 if (buddy == null) {
700 return;
701 }
702
703 logger.fine("Attempting to warn: " + buddy.getName() + ".");
704
705 String work = "toc_evil ";
706 work = work.concat(imNormalize(buddy.getName()));
707 work = work.concat(" norm \0");
708
709 try {
710 frameSend(work);
711 } catch (IOException e) {
712 signoff("9");
713 }
714 }
715
716
717 /***
718 * tell aim to ignore a buddy
719 *
720 * @param buddy
721 */
722 public void banBuddy(AIMBuddy buddy) {
723 if ((buddy == null) || (buddy.getName().length() == 0)) {
724 return;
725 }
726
727 if (getBuddy(buddy.getName()) == null) {
728 return;
729 }
730 buddy.setBanned(true);
731 sendDeny(imNormalize(buddy.getName()));
732 }
733
734
735 /***
736 * tell aim to ignore a buddy
737 *
738 * @param buddyname
739 */
740 private void sendDeny(String buddyname) {
741 if (buddyname.length() == 0) {
742 logger.fine("Attempting to permit all.");
743 } else {
744 logger.fine("Attempting to deny: " + buddyname + ".");
745 }
746
747 String toBeSent = "toc2_add_deny";
748 try {
749 frameSend(toBeSent + " " + buddyname + "\0");
750 } catch (IOException e) {
751 logger.severe(e.toString());
752 signoff("7.75");
753 }
754 }
755
756
757 /***
758 * tell aim to permit a buddy
759 *
760 * @param buddyname
761 */
762 private void sendPermit(String buddyname) {
763 logger.fine("Attempting to permit: " + buddyname + ".");
764
765 String toBeSent = "toc2_add_permit";
766 try {
767 frameSend(toBeSent + " " + buddyname + "\0");
768 } catch (IOException e) {
769 logger.severe(e.getMessage());
770 signoff("7.875");
771 }
772 }
773
774
775 /***
776 * protocol methods *
777 *
778 * @param toBeSent
779 * @exception IOException
780 * Description of Exception
781 */
782 private void frameSend(String toBeSent) throws IOException {
783 if (sendLimit < MAX_POINTS) {
784 sendLimit += ((System.currentTimeMillis() - lastFrameSendTime) / RECOVER_RATE);
785
786
787 sendLimit = Math.min(MAX_POINTS, sendLimit);
788 if (sendLimit < MAX_POINTS) {
789
790 logger.fine("Current send limit=" + sendLimit + " out of " + MAX_POINTS);
791 try {
792
793 int waitAmount = MAX_POINTS - sendLimit;
794 logger.fine("Delaying send " + waitAmount + " units");
795 Thread.sleep(RECOVER_RATE * waitAmount);
796 sendLimit += waitAmount;
797 } catch (InterruptedException ie) {
798 }
799 }
800 }
801 out.writeByte(42);
802 out.writeByte(2);
803 out.writeShort(seqNo);
804 seqNo = (seqNo + 1) & 65535;
805 out.writeShort(toBeSent.length());
806 out.writeBytes(toBeSent);
807 out.flush();
808
809
810
811 int warnAmount = getBuddy(this.name).getWarningAmount();
812 sendLimit -= (1 + Math.pow((3 * warnAmount) / 100, 2));
813 lastFrameSendTime = System.currentTimeMillis();
814 }
815
816
817 /***
818 * Send message event to all listeners.
819 *
820 * @param from
821 * @param request
822 */
823 private void generateMessage(String from, String request) {
824 AIMBuddy aimbud = getBuddy(from);
825 if (aimbud == null) {
826 if (autoAddUsers) {
827 aimbud = new AIMBuddy(from);
828 addBuddy(aimbud);
829 aimbud.setOnline(true);
830 } else {
831 logger.info("MESSAGE FROM A NON BUDDY(" + from + ")");
832
833 if ((nonUserResponse != null) && !nonUserResponse.equals("")) {
834 sendMesg(from, nonUserResponse);
835 }
836 return;
837 }
838 }
839
840 if (aimbud.isBanned()) {
841 logger.fine("Ignoring message from banned user (" + from + "):" + request);
842 } else {
843 for (int i = 0; i < aimListeners.size(); i++) {
844 try {
845 ((AIMListener) aimListeners.get(i)).handleMessage(aimbud, request);
846 } catch (Exception e) {
847 e.printStackTrace();
848 }
849 }
850 }
851 }
852
853
854 /***
855 * Send warning event to all listeners.
856 *
857 * @param from
858 * @param amount
859 * of warning
860 */
861 private void generateWarning(String from, int amount) {
862 AIMBuddy aimbud = getBuddy(from);
863 for (int i = 0; i < aimListeners.size(); i++) {
864 try {
865 ((AIMListener) aimListeners.get(i)).handleWarning(aimbud, amount);
866 } catch (Exception e) {
867 e.printStackTrace();
868 }
869 }
870 }
871
872
873 /***
874 * Send connected event to all listeners.
875 */
876 private void generateConnected() {
877 for (int i = 0; i < aimListeners.size(); i++) {
878 try {
879 ((AIMListener) aimListeners.get(i)).handleConnected();
880 } catch (Exception e) {
881 e.printStackTrace();
882 }
883 }
884 }
885
886
887 /***
888 * Send disconnected event to all listeners.
889 */
890 private void generateDisconnected() {
891 for (int i = 0; i < aimListeners.size(); i++) {
892 try {
893 ((AIMListener) aimListeners.get(i)).handleDisconnected();
894 } catch (Exception e) {
895 e.printStackTrace();
896 }
897 }
898 }
899
900
901 /***
902 * Send error event to all listeners.
903 *
904 * @param error
905 * code
906 * @param message
907 */
908 private void generateError(String error, String message) {
909 for (int i = 0; i < aimListeners.size(); i++) {
910 try {
911 ((AIMListener) aimListeners.get(i)).handleError(error, message);
912 } catch (Exception e) {
913 e.printStackTrace();
914 }
915 }
916 }
917
918
919 /***
920 * Send buddy sign on event to all listeners.
921 *
922 * @param buddy
923 * that signed on
924 * @param message
925 */
926 private void generateBuddySignOn(String buddy, String message) {
927 AIMBuddy aimbud = getBuddy(buddy);
928 if (aimbud == null) {
929 logger.severe("ERROR: NOTIFICATION ABOUT NON BUDDY(" + buddy + ")");
930 return;
931 }
932
933 if (!aimbud.isOnline()) {
934 aimbud.setOnline(true);
935 for (int i = 0; i < aimListeners.size(); i++) {
936 try {
937 ((AIMListener) aimListeners.get(i)).handleBuddySignOn(aimbud, message);
938 } catch (Exception e) {
939 e.printStackTrace();
940 }
941 }
942 }
943 }
944
945
946 /***
947 * Send buddy sign off event to all listeners.
948 *
949 * @param buddy
950 * that signed off
951 * @param message
952 */
953 private void generateBuddySignOff(String buddy, String message) {
954 AIMBuddy aimbud = getBuddy(buddy);
955 if (aimbud == null) {
956 logger.severe("ERROR: NOTIFICATION ABOUT NON BUDDY(" + buddy + ")");
957 return;
958 }
959
960
961 aimbud.setOnline(false);
962 for (int i = 0; i < aimListeners.size(); i++) {
963 try {
964 ((AIMListener) aimListeners.get(i)).handleBuddySignOff(aimbud, message);
965 } catch (Exception e) {
966 e.printStackTrace();
967 }
968 }
969 }
970
971
972 /***
973 * Send buddy is available event to all listeners.
974 *
975 * @param buddy
976 * The subject of the change.
977 * @param message
978 * DOCUMENT ME!
979 */
980 private void generateBuddyAvailable(String buddy, String message) {
981 AIMBuddy aimbud = getBuddy(buddy);
982 if (aimbud == null) {
983 logger.severe("ERROR: NOTIFICATION ABOUT NON BUDDY(" + buddy + ")");
984 return;
985 }
986 for (int i = 0; i < aimListeners.size(); i++) {
987 try {
988 ((AIMListener) aimListeners.get(i)).handleBuddyAvailable(aimbud, message);
989 } catch (Exception e) {
990 e.printStackTrace();
991 }
992 }
993 }
994
995
996 /***
997 * Send buddy is unavailable event to all listeners.
998 *
999 * @param buddy
1000 * The subject of the change.
1001 * @param message
1002 * DOCUMENT ME!
1003 */
1004 private void generateBuddyUnavailable(String buddy, String message) {
1005 AIMBuddy aimbud = getBuddy(buddy);
1006 if (aimbud == null) {
1007 logger.severe("ERROR: NOTIFICATION ABOUT NON BUDDY(" + buddy + ")");
1008 return;
1009 }
1010
1011 for (int i = 0; i < aimListeners.size(); i++) {
1012 try {
1013 ((AIMListener) aimListeners.get(i)).handleBuddyUnavailable(aimbud, message);
1014 } catch (Exception e) {
1015 e.printStackTrace();
1016 }
1017 }
1018 }
1019
1020
1021 /***
1022 * message recieved from aim
1023 *
1024 * @param buffer
1025 */
1026 private void fromAIM(byte[] buffer) {
1027 try {
1028 String inString = new String(buffer);
1029
1030 logger.fine("*** AIM: " + inString + " ***");
1031 StringTokenizer inToken = new StringTokenizer(inString, ":");
1032 String command = inToken.nextToken();
1033 if (command.equals("IM_IN2")) {
1034
1035 this.connectionVerified = true;
1036
1037 String from = imNormalize(inToken.nextToken());
1038
1039 inToken.nextToken();
1040
1041 inToken.nextToken();
1042 String mesg = inToken.nextToken();
1043 while (inToken.hasMoreTokens()) {
1044 mesg = mesg + ":" + inToken.nextToken();
1045 }
1046
1047 String request = stripHTML(mesg);
1048
1049 if ((from.equalsIgnoreCase(this.name)) && (request.equals(AIMClient.PING))) {
1050 logger.fine("AIM CONNECTION VERIFIED(" + new Date() + ").");
1051 return;
1052 }
1053
1054 logger.fine("*** AIM MESSAGE: " + from + " > " + request + " ***");
1055
1056
1057 generateMessage(from, request.trim());
1058 return;
1059 }
1060
1061 if (command.equals("CONFIG2")) {
1062 if (inToken.hasMoreElements()) {
1063 String config = inToken.nextToken();
1064 while (inToken.hasMoreTokens()) {
1065 config = config + ":" + inToken.nextToken();
1066 }
1067 processConfig(config);
1068 logger.fine("*** AIM CONFIG RECEIVED ***");
1069 } else {
1070 setPermitMode(PERMIT_ALL);
1071 logger.fine("*** AIM NO CONFIG RECEIVED ***");
1072 }
1073 return;
1074 }
1075
1076 if (command.equals("EVILED")) {
1077 int amount = Integer.parseInt(inToken.nextToken());
1078 String from = "anonymous";
1079 if (inToken.hasMoreElements()) {
1080 from = imNormalize(inToken.nextToken());
1081 }
1082
1083
1084
1085
1086 if (getBuddy(name).getWarningAmount() < amount) {
1087 generateWarning(from, amount);
1088 }
1089
1090 return;
1091 }
1092
1093 if (command.equals("UPDATE_BUDDY2")) {
1094 String bname = imNormalize(inToken.nextToken());
1095 AIMBuddy aimbud = getBuddy(bname);
1096 if (aimbud == null) {
1097 logger.severe("ERROR: NOTIFICATION ABOUT NON BUDDY(" + bname + ")");
1098 return;
1099 }
1100 String stat = inToken.nextToken();
1101 if (stat.equals("T")) {
1102 generateBuddySignOn(bname, "INFO");
1103
1104 } else if (stat.equals("F")) {
1105 generateBuddySignOff(bname, "INFO");
1106
1107 }
1108 int evilAmount = Integer.parseInt(inToken.nextToken());
1109 aimbud.setWarningAmount(evilAmount);
1110 if (stat.equals("T")) {
1111 String signOnTime = inToken.nextToken();
1112
1113
1114
1115 String idleTime = inToken.nextToken();
1116
1117
1118 if (-1 != inToken.nextToken().indexOf('U')) {
1119 generateBuddyUnavailable(bname, "INFO");
1120 } else {
1121 generateBuddyAvailable(bname, "INFO");
1122 }
1123 }
1124
1125 return;
1126 }
1127
1128 if (command.equals("ERROR")) {
1129 String error = inToken.nextToken();
1130 logger.severe("*** AIM ERROR: " + error + " ***");
1131 if (error.equals("901")) {
1132 generateError(error, "Not currently available");
1133
1134 return;
1135 }
1136
1137 if (error.equals("902")) {
1138 generateError(error, "Warning not currently available");
1139
1140 return;
1141 }
1142
1143 if (error.equals("903")) {
1144 generateError(error, "Message dropped, sending too fast");
1145
1146 return;
1147 }
1148
1149 if (error.equals("960")) {
1150 String person = inToken.nextToken();
1151 generateError(error, "Sending messages too fast to " + person);
1152
1153 return;
1154 }
1155
1156 if (error.equals("961")) {
1157 String person = inToken.nextToken();
1158 generateError(error, person + " sent you too big a message");
1159
1160 return;
1161 }
1162
1163 if (error.equals("962")) {
1164 String person = inToken.nextToken();
1165 generateError(error, person + " sent you a message too fast");
1166
1167 return;
1168 }
1169
1170 if (error.equals("Signon err")) {
1171 String text = inToken.nextToken();
1172 generateError(error, "AIM Signon failure: " + text);
1173
1174
1175 signoff("5");
1176 }
1177
1178 return;
1179 }
1180 } catch (Exception e) {
1181 logger.severe("ERROR: failed to handle aim protocol properly");
1182 e.printStackTrace();
1183 }
1184 }
1185
1186
1187 /***
1188 * Processes AIM server-passed config string
1189 *
1190 * @param config
1191 * A properly formated TOC configuration.
1192 */
1193 private void processConfig(String config) {
1194 int new_permit_mode = PERMIT_ALL;
1195 BufferedReader br = new BufferedReader(new StringReader(config));
1196 try {
1197 String current_group = DEFAULT_GROUP;
1198 String line;
1199 while (null != (line = br.readLine())) {
1200 if (line.equals("done")) {
1201 break;
1202 }
1203 char type = line.charAt(0);
1204 String arg = line.substring(2);
1205 switch (type) {
1206 case 'g' :
1207 current_group = arg;
1208 break;
1209 case 'b' :
1210
1211 AIMBuddy buddy = null;
1212 buddy = (AIMBuddy) buddyHash.get(imNormalize(arg));
1213 if (buddy == null) {
1214 buddy = new AIMBuddy(arg, current_group);
1215 buddyHash.put(imNormalize(arg), buddy);
1216 } else {
1217
1218
1219
1220 buddy.setGroup(current_group);
1221 }
1222 break;
1223 case 'p' :
1224 permitted.add(imNormalize(arg));
1225 break;
1226 case 'd' :
1227 denied.add(imNormalize(arg));
1228 break;
1229 case 'm' :
1230 new_permit_mode = Integer.parseInt(arg);
1231 break;
1232 }
1233 }
1234 } catch (IOException e) {
1235 logger.warning("Error reading configuration.");
1236 signoff("2.25");
1237 return;
1238 }
1239
1240
1241 addBuddies(new ArrayList(buddyHash.values()));
1242 setPermitMode(new_permit_mode);
1243 }
1244
1245
1246 /***
1247 * internal method to send message to aim
1248 *
1249 * @param to
1250 * @param text
1251 * to send
1252 */
1253 void sendMesg(String to, String text) {
1254 if (text.length() >= 1024) {
1255 text = text.substring(0, 1024);
1256 }
1257 logger.fine("Sending Message " + to + " > " + text);
1258
1259 String work = "toc2_send_im ";
1260 work = work.concat(to);
1261 work = work.concat(" \"");
1262 for (int i = 0; i < text.length(); i++) {
1263 switch (text.charAt(i)) {
1264 case '$' :
1265 case '{' :
1266 case '}' :
1267 case '[' :
1268 case ']' :
1269 case '(' :
1270 case ')' :
1271 case '\"' :
1272 case '//' :
1273 work = work.concat("//" + text.charAt(i));
1274 break;
1275 default :
1276 work = work.concat("" + text.charAt(i));
1277 break;
1278 }
1279 }
1280
1281 work = work.concat("\"\0");
1282
1283 try {
1284 frameSend(work);
1285 } catch (IOException e) {
1286 logger.severe("*** AIM ERROR: sending message.");
1287 e.printStackTrace();
1288 signoff("9");
1289 }
1290 }
1291
1292
1293 /***
1294 * Change availability. If the reason is the empty String, the user will be
1295 * made avaiable. Otherwise, it will be made away.
1296 *
1297 * @param reason
1298 * The reason explaining why the user is not avaiable.
1299 */
1300 private void sendAway(String reason) {
1301 final String work = "toc_set_away \"" + reason + "\"\0";
1302
1303 try {
1304 frameSend(work);
1305 } catch (IOException e) {
1306 signoff("10");
1307 }
1308 }
1309
1310
1311 /***
1312 * sign off
1313 *
1314 * @param place
1315 */
1316 private void signoff(String place) {
1317 online = false;
1318 logger.fine("Trying to close IM (" + place + ").....");
1319 try {
1320 if (null != out) {
1321 out.close();
1322 }
1323 if (null != in) {
1324 in.close();
1325 }
1326 if (null != connection) {
1327 connection.close();
1328 }
1329 } catch (IOException e) {
1330 logger.severe(e.toString());
1331 }
1332
1333 generateDisconnected();
1334 logger.fine("*** AIM CLIENT SIGNED OFF.");
1335 }
1336
1337
1338 /***
1339 * Add a buddy to the denied list
1340 *
1341 * @param buddy
1342 */
1343 public void denyBuddy(AIMBuddy buddy) {
1344 String bname = imNormalize(buddy.getName());
1345 permitted.remove(bname);
1346 denied.add(bname);
1347 sendDeny(bname);
1348 }
1349
1350
1351 /***
1352 * Add a buddy to the permitted list
1353 *
1354 * @param buddy
1355 */
1356 public void permitBuddy(AIMBuddy buddy) {
1357 String bname = imNormalize(buddy.getName());
1358 denied.remove(bname);
1359 permitted.add(bname);
1360 sendPermit(bname);
1361 }
1362
1363
1364 /***
1365 * Gets the permit mode that is set on the server.
1366 *
1367 * @return int representation (see public statics) of current permit mode.
1368 */
1369 public int getPermitMode() {
1370 return permitMode;
1371 }
1372
1373
1374 /***
1375 * Sets the permit mode on the server. (Use constants from AIMSender)
1376 *
1377 * @param mode
1378 */
1379 public void setPermitMode(int mode) {
1380 if (mode < 1 || mode > 5) {
1381 logger.info("Invalid permit mode, ignoring:" + mode);
1382 return;
1383 } else if (mode == DENY_SOME && this.denied.size() == 0) {
1384 logger.info("Attempting to deny some, and none are denied, ignoring.");
1385 return;
1386 } else if (mode == PERMIT_SOME && this.permitted.size() == 0) {
1387 logger.info("Attempting to permit some, and none are permitted, ignoring.");
1388 return;
1389 }
1390
1391 logger.info("Setting permit mode to:" + mode);
1392 permitMode = mode;
1393 try {
1394 frameSend("toc2_set_pdmode " + permitMode + "\0");
1395 } catch (IOException e) {
1396 e.printStackTrace();
1397 logger.severe("ERROR setting permit mode!");
1398 }
1399 }
1400
1401
1402 /***
1403 * Clear unvailable message
1404 */
1405 public void setAvailable() {
1406 sendAway("");
1407 }
1408
1409
1410 /***
1411 * Set unvailable message
1412 *
1413 * @param reason
1414 */
1415 public void setUnavailable(String reason) {
1416 sendAway(reason);
1417 }
1418
1419
1420 /***
1421 * Try to verify our connections by messaging ourselves. Takes 2 instances
1422 * to check. One to send the message (sender=true), One to check the result
1423 * (sender=false)
1424 *
1425 * @author Scott Oster
1426 * @created February 28, 2002
1427 */
1428 static class AimConnectionCheck extends TimerTask {
1429 AIMClient aim;
1430 private boolean sender;
1431
1432
1433 /***
1434 * Constructor for the AimConnectionCheck object
1435 *
1436 * @param aim
1437 * handle to aim
1438 * @param sender
1439 * which instance it is
1440 */
1441 public AimConnectionCheck(AIMClient aim, boolean sender) {
1442 this.aim = aim;
1443 this.sender = sender;
1444 }
1445
1446
1447 /***
1448 * Main processing method for the AimConnectionCheck object
1449 */
1450 public void run() {
1451 try {
1452 if (sender) {
1453 aim.connectionVerified = false;
1454
1455
1456 if (aim.online) {
1457 aim.sendMesg(aim.name, AIMClient.PING);
1458 }
1459 } else {
1460
1461 if (!aim.connectionVerified) {
1462
1463 logger.info("*** AIM -- CONNECTION PROBLEM(" + new Date() + "): Connection was not verified!");
1464 logger.info("****** Assuming it was dropped, issuing restart.");
1465 aim.signoff("Connection Dropped!");
1466 new Thread(aim).start();
1467 }
1468 }
1469 } catch (Exception e) {
1470 e.printStackTrace();
1471 }
1472 }
1473 }
1474 }