Global:

Handling user join/leave is slow, both on the client and the server
(although it is worse on the client).

rld:

--- Fix:

* Rework quirky transfer queueing, get rid of giant lock. (fixed, just
  two problems remain, 1: queue is not fair, 2: server doesn't notice
  when people remove their queue entry).
* Drop boxes are not handled properly.
* Privileges are screwed. (done)
* Upload from 1.8.5 client can't complete (file size 16 bytes too large?)
  (think fixed)
* There's a critical (dead server) deadlock that can occur in 
  HLServerDispatcher.dispatchLogin() when two users connect shortly 
  after one another:

  1. User A logs in, locks his userStateLock, allocates a socket ID, 
     and finally enters changeUserExcept().
  2. User B logs in, locks his userStateLock.
  3. User A enters realmTable.broadcastPacketExcept(), 
     which is a synchronized method in realmTable. 
  4. User B enters allocateSocketID(), which blocks as it
     tries to enter realmTable.addUser(), because addUser() is
     also a sync. method in realmTable.
  5. User A enters resolveSocketID(), which blocks the moment 
     it tries to getUser() on B, because User B holds the
     userStateLock.
  6. User B cannot release the userStateLock until User A leaves
     realmTable.broadcastPacketExcept(), which it cannot do
     until User B releases the userStateLock -> deadlock.

  (see dump 2).

  Solution: resolveSocketID() should not synchronize on 
  userStateLock, HLServerDispatcher.sock is a read-only var. (done)

* erk reported:
  "When I try to open the drop box without privs it refreshes the folder
  list instead. This causes all the open folders in my 1.8.5 client's 
  file tree to close" (should not return an error message, just an 
  empty list)
* Many packets get 0 as their transaction ID. This doesn't seem
  to harm anything but it would be better if it just increased
  monotonically. (fixed, harms userlist handling btw).
* User join/leave not always correct (sometimes a HL 1.8 client
  may not see a user leave). rl does not have this problem, so it's
  probably a transaction ID problem, see above.
* View in 1.8.5 does not work well at all. (done)
* Chat without privileges generates task error, should generate
  admin message.
  
--- Add:

* .lnk files should get type "alis" until rld has support for
  Windows shortcuts.
* Show transfer info in status and user info. (done)
* Make alias. (try something with System.exec("ln -s ...") (or perhaps not))
* Move files / folders. (done)
* Sort file list. (done)
* Time out people who connect to the transferserver but don't 
  start transferring. (done)
* Transfer queueing + max transfers etcetera. (done)
* Set file info. (done, but SplitMacFile ignores writeHeader())
* Get user info. (done, doesn't show transfers yet)
* Agreement. (done)
* Time out people who connect but don't log in. (done)
* Time out transfers that are requested but never appear in the
  transferserver. (done, untested, done)
* Private chat subject. (done)
* Flood banning. (done, disconnects instead of banning)
* Admin CLI interface. (done)

rl:

--- Add:

* Threaded news.
* Directory downloading. (done)
* Drag and drop. (done, sort of)

--- Fix:

High priority:

* Trackers are apparently not saved.
* Sometimes you show up on HL 1.8 servers blank, and user changes
  like <<< groomed is now known as >>> appear.
* Dragging to download is broken.
* Add confirmation window for "Close connection" (Control-W).
* Look at invokeAndWait() races.
* Partial files need .hpf extension. (done)
* At least 1.8 servers report "Received unknown transaction type!" 
  when you log in. (done) (broken again, fix breaks other things,
  had to put sendAgree() back)
* Quit-dialog while transfers in progress shows too late, after the
  shell window has already closed (Windows, done).
* There's a race somewhere (chat doesn't get sent, file lists
  don't arrive anymore) (perhaps solved by getting rid of the
  WriterThread). (done, needed to synchronize on outputstream)
* MacOS Classic package doesn't work on MacOS 9.1 (get rid of 
  NativeMacFile??) (contact malgow@mac.com when fixed) (drop Classic)
  (drop JDK 1.2)
* Check whether files returned from file selectors actually exists.
* MacFile's created but for which no task exists never get cleaned up (ie. 
  locally queued downloads) (think done).
* Private chat window refuses to close under certain circumstances
  (when being disconnected from the server). (difficult to reproduce)
* Error while "Quit yes / no?" locks up app, neither can get focus
  and because they're both modal, the shell windows cannot get focus
  either.
* FileSeeker must have a limit on the number of files that it seeks
  in addition to a limit on recursion depth. (done, limit set to 20000)
* Delete multiple files does not work, asks to delete every file separately,
  and hangs Tube when an error box appears. (done)
* Race between waitFor() & ReaderThread(). (done, but needs testing +
  documentation) (think done) (done)
* Chat string containing / as first char is not sent.  (not a bug, just 
  the way hxd works)
* Right-click to privchat invite doesn't work on Windows. (done)
* FileSeeker results don't work. (done)
  
Low priority:

* FileSeeker needs context menus and buttons.
* Fix context menu selection method.
* Following links in HTML view is broken (links are not always
  correctly resolved).
* Fix "0 queue" bug on 1.8.4 servers.
* Encrypt passwords in bookmarks.
* Check for dead connection after displaying modal dialogs 
  (filechoosers etc.)
* Scrollbars are not always properly setup when viewing images.
* Dispatch every Hotline event in the same thread (event stack) 
  (compromise: allow handleTaskComplete and handleTaskError to occur in
  different thread, for file transfers. that leaves just handleDisconnect 
  to fix, because that one on rare occasions may still occur from 
  WriterThread) (done, got rid of WriterThread).
* Change nick only changes nick in first tab / nick should be a per-server
  option. (done)
* Don't leave private chat when join failed.
* Audio hogging. (done, just turned it off, needs Sun fix)
* Images are not always fully transferred when viewing them. (done)
* Bug with error handling (TubeConnection.error()?) if strings become 
  too large or something like that. (sort of done, textWrap still 
  fuxored) (textWrap improved)
* We never wake up waiting for agreement if the server goes down. (? unsure
  if still true)
* Fix HLClient thread safety issues. (new single-thread implementation
  should greatly improve this)
* Check Swing thread safety issues. (see above)
* MacFile really needs to be lazier. (SplitMacFile is lazy now) 
* Apparently there is a condition where the Hotline server thinks
  Tube is no longer downloading, but where Tube thinks it is still 
  downloading (symptom: the transfer progress stops entirely for a 
  long period of time). Cancelling such a "ghost" download hangs Tube.
  (depends on proper interplay between setSoTimeout() and 
  InterruptableInputStream, perhaps fixed already) (done)
* Check whether bookmarks can contain hostnames -> Yes, they can, but
  resolving an IP to a hostname can take some time, perhaps this needs
  a UI fix.
* Fix input focus (set focus on [most on important child of] the last 
  clicked element). (focus is just generally a mess)
* Fix nick mangling (garbage at the end) on some servers. (may very well
  be solved)
* Fix interface construction build speed (lazy construction of 
  Tube*Interface). (see LazyPanel)
* Fix documentation.
* Fix possible memory leakage (try removeActionListener etcetera). (seems 
  to work reasonably well with -Xmx5 or so. perhaps the JVM just never
  releases memory unless it absolutely has to) (yep, even worse, it just
  "never" releases memory)
* Quit is not very graceful, it doesn't wait for connections to be 
  closed before quitting (and the HLClient object doesn't provide
  methods to deal with this). (1.3 has System.addShutdownHook(), something
  to remember) (also, HLClient doesn't create so many threads
  anymore)
* Action strings in chat with hotkey. (? maybe /me is just fine) (yes, /me
  is just fine)
* Use average over last minute to calculate transfer speed. (fixed) 
  (doesn't really use average yet, but used to include the time waited
  while a transfer was queued, so it's a lot better already)
* Sometimes uploads do not seem to complete. (untested fix) (think fixed)
* Adapt FilenameQualifier.qualify() for Windows. (untested fix) (done)
* NativeMacFile.delete() does not work because the forks need to be closed
  before the file is deleted. (untested fix)

--- Add:

* Default bookmark for Tube Hotline site. (done, in Help)
* Hostname / bookmarks on commandline. (done)
* Help menu with keyboard map and about box. (done)
* Add browse... buttons to Options->Paths. (done)
* Add keyboard shortcuts.
* Add more tooltips.
* Add input focus indicators. (won't)

--- Unexpected exceptions:

java.lang.IllegalArgumentException: socket ID 851 already in realm 1854328482
at redlight.hotline.HLServer$RealmTable$Realm.addUser(HLServer.java:2135)
at redlight.hotline.HLServer$RealmTable.addUser(HLServer.java:1870)
at redlight.hotline.HLServerDispatcher.dispatchPrivateChatJoin(HLServerDispatcher.java:2242)
at redlight.hotline.HLServerDispatcher.dispatch(HLServerDispatcher.java:554)
at redlight.hotline.HLServerDispatcher.run(HLServerDispatcher.java:314)
*** Happened completely unexpectedly during private chat. Maybe just 
    ignore it when the same socket ID is added to a realm? (ignoring)
    
java.lang.NullPointerException
at groomed.tube.TubeTransferInterface.<init>(TubeTransferInterface.java:118)
at groomed.tube.TubeDownloadInterface.<init>(TubeDownloadInterface.java:62)
at groomed.tube.TubeDownloadInterface.<init>(TubeDownloadInterface.java:53)
*** May happen ... 

java.lang.NullPointerException
at groomed.hotline.HLServer$TransferQueue$TransferRequest.toString(HLServer.java:1592)
at java.lang.String.valueOf(String.java:1925)
at java.lang.StringBuffer.append(StringBuffer.java:373)
at groomed.hotline.HLServer$TransferQueue.expire(HLServer.java:1553)
at groomed.hotline.HLServerMonitor.run(HLServer.java:1952)
*** Can't happen anymore

groomed.utils.DebuggerException: removeTaskErrorHandler: cannot remove task 10 because it does not exist
at groomed.utils.DebuggerOutput.tell(DebuggerOutput.java:102)
at groomed.tube.TubeMachine.removeTaskErrorHandler(TubeMachine.java:218)
at groomed.tube.TubeTransferInterface.dispose(TubeTransferInterface.java:271)
at groomed.tube.TubeTransferInterface.close(TubeTransferInterface.java:344)
at groomed.tube.TubeTransferInterface.closeUnregister(TubeTransferInterface.java:316)
at groomed.tube.TubeTransferInterface.closeUnregister(TubeTransferInterface.java:323)
at groomed.tube.TubeDownloadInterface$ResumeUI.run(TubeDownloadInterface.java:219)
*** Think done.

groomed.utils.DebuggerException: ScriptBroker: illegal target remove for groomed.tube.TubeDownloadInterface@c666a
at groomed.utils.DebuggerOutput.tell(DebuggerOutput.java:102)
at groomed.script.ScriptBroker.removeTarget(ScriptBroker.java:77)
at groomed.tube.TubeTransferInterface.dispose(TubeTransferInterface.java:301)
at groomed.tube.TubeTransferInterface.close(TubeTransferInterface.java:344)
at groomed.tube.TubeTransferInterface.closeUnregister(TubeTransferInterface.java:316)
at groomed.tube.TubeTransferInterface.closeUnregister(TubeTransferInterface.java:323)
at groomed.tube.TubeTransferInterface.stopMeterWithError(TubeTransferInterface.java:466)
at groomed.hotline.HLEventReceiver.recvFileGet(HLEventReceiver.java:602)
at groomed.hotline.HLEventReceiver.recvTask(HLEventReceiver.java:873)
at groomed.hotline.Dispatcher.run(HLClientConnection.java:2691)
at java.lang.Thread.run(Thread.java:484)
*** Can't happen anymore.

java.lang.NullPointerException
at redlight.graphics.Spinner.stop(Spinner.java:119)
at redlight.client.TrackerInterface$3.run(TrackerInterface.java:534)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:154)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:334)
*** Think done.

java.lang.NegativeArraySizeException: 
at redlight.hotline.HLProtocol$Packet.<init>(HLProtocol.java:260)
at redlight.hotline.HLServerDispatcher.run(HLServerDispatcher.java:314)
*** Think done.

java.lang.NullPointerException
	at redlight.client.Shell$4.run(Shell.java:231)
	at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:167)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:437)
	at java.awt.EventDispatchThread.pumpOneEvent(EventDispatchThread.java:150)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:136)	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:131)	at java.awt.Dialog.show(Dialog.java:498)
	at javax.swing.JOptionPane.showOptionDialog(JOptionPane.java:802)
	at javax.swing.JOptionPane.showConfirmDialog(JOptionPane.java:733)
	at javax.swing.JOptionPane.showConfirmDialog(JOptionPane.java:698)
	at redlight.client.Main$1.handleQuit(Main.java:90)
	at redlight.utils.SystemEvents.handleQuit(SystemEvents.java:41)
	at redlight.client.Main.removeShell(Main.java:276)
	at redlight.client.Shell.windowClosing(Shell.java:518)
	at java.awt.Window.processWindowEvent(Window.java:1060)
	at javax.swing.JFrame.processWindowEvent(JFrame.java:264)
*** Closing the shell while transfers were active first displayed
    a 'Close?' dialog, then this exception, then a 'Quit?' dialog.

--- Thread dumps:


*** Thread dump at a point where the connection fails so quickly
    that the disconnect message doesn't seem to arrive or sth ...

    --> This was a race between HLClient.connect() and HLClient.login()
        resulting in deadlock:
    
       1. Machine.connect() acquires a lock L
       2. Machine.connect() calls HLClient.connect()
       3. HLClient.connect() starts ReaderThread
       4. ReaderThread tries to read a packet and immediately fails.
       5. ReaderThread goes into internalDisconnect(), from there into
          terminateAllTasks() and also Machine.handleDisconnect().
       6. Machine.connect() calls HLClient.login()
       7. HLClient.login() creates a login task and waits for
          a reply to come back, which never comes.
       8. Machine.handleDisconnect() calls Machine.disconnect()
       9. Machine.disconnect() waits for Machine.connect() to
          release lock L...
	  
       ...but Machine.connect() cannot release lock L, because 
       HLClient.login() will wait forever. So, deadlock results.

Full thread dump:

"Thread-25" prio=1 tid=0x0x8150a28 nid=0x42bc waiting for monitor entry
[4e16d000..4e16d890]
		    at redlight.client.Machine.disconnect(Machine.java:175)
		       - waiting to lock <0x445f79a0> (a redlight.client.Machine)
		       	 at redlight.client.Machine.handleDisconnect(Machine.java:872)
			    at redlight.hotline.HLClient$1.run(HLClient.java:2225)
			       at java.lang.Thread.run(Thread.java:579)
			       
			       "Spinner sun.awt.motif.X11Image@4df764"
prio=1 tid=0x0x8387800 nid=0x42b8 waiting on monitor [4d96d000..4d96d890]
       at java.lang.Thread.sleep(Native Method)
       	  at redlight.graphics.Spinner.run(Spinner.java:68)
	     at java.lang.Thread.run(Thread.java:579)
	     
	     "QueueThread" prio=1 tid=0x0x8387258 nid=0x42b6 waiting on
monitor [4c96d000..4c96d890]
	at java.lang.Object.wait(Native Method)
	   - waiting on <0x445f78d0> (a redlight.utils.QueueThread)
	     at redlight.utils.QueueThread.run(QueueThread.java:170)
	     	- locked <0x445f78d0> (a redlight.utils.QueueThread)
		  at java.lang.Thread.run(Thread.java:579)
		  
		  "Thread-21" prio=1 tid=0x0x816b820 nid=0x42b5 waiting on
monitor [4c0f0000..4c0f0890]  at java.lang.Object.wait(Native Method)
	- waiting on <0x44616838> (a redlight.hotline.HLTask$SynchronizedContainer)
	  at java.lang.Object.wait(Object.java:425)
	     at redlight.hotline.HLTask$SynchronizedContainer.get(HLTask.java:38)
	     	- locked <0x44616838> (a redlight.hotline.HLTask$SynchronizedContainer)
		  at redlight.hotline.HLTask.getData(HLTask.java:75)
		     at redlight.hotline.HLClient.login(HLClient.java:602)
		     	at redlight.hotline.HLClient.login(HLClient.java:547)
			   at redlight.client.Machine.connect(Machine.java:113)
			      - locked <0x445f79a0> (a redlight.client.Machine)
			      	at redlight.client.Connector.run(Connector.java:93)
				   at java.lang.Thread.run(Thread.java:579)
				   
				   "TimerQueue" daemon prio=1
tid=0x0x82d6e88 nid=0x4291 waiting on monitor [4c56d000..4c56d890]
		at java.lang.Object.wait(Native Method)
		   - waiting on <0x444241f0> (a javax.swing.TimerQueue)
		     at javax.swing.TimerQueue.run(TimerQueue.java:234)
		     	- locked <0x444241f0> (a javax.swing.TimerQueue)
			  at java.lang.Thread.run(Thread.java:579)
			  
			  "Thread-4" prio=1 tid=0x0x80541e8 nid=0x4266
waiting on monitor [0..bfffd610]

"Thread-1" daemon prio=1 tid=0x0x8342560 nid=0x428d waiting on monitor
[4bef0000..4bef0890]
		    at java.lang.Object.wait(Native Method)
		       - waiting on <0x4438f548> (a java.util.TaskQueue)
		       	 at java.util.TimerThread.mainLoop(Timer.java:432)
			    - locked <0x4438f548> (a java.util.TaskQueue)
			      at java.util.TimerThread.run(Timer.java:385)
			      
			      "AWT-EventQueue-0" prio=1 tid=0x0x8207000
nid=0x428c waiting on monitor [4bcf0000..4bcf0890]
	   at java.lang.Object.wait(Native Method)
	      - waiting on <0x44322de8> (a java.awt.EventQueue)
	      	at java.lang.Object.wait(Object.java:425)
		   at java.awt.EventQueue.getNextEvent(EventQueue.java:325)
		      - locked <0x44322de8> (a java.awt.EventQueue)
		      	at
java.awt.EventDispatchThread.pumpOneEvent(EventDispatchThread.java:145)
								       at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:136)
								     at
java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:131)
								     at java.awt.EventDispatchThread.run(EventDispatchThread.java:99)
								     

"AWT-Shutdown" prio=1 tid=0x0x8206e30 nid=0x428b waiting on monitor
[4b8ec000..4b8ec890]
		    at java.lang.Object.wait(Native Method)
		       - waiting on <0x443023e8> (a java.lang.Object)
		       	 at java.lang.Object.wait(Object.java:425)
			    at sun.awt.AWTAutoShutdown.run(AWTAutoShutdown.java:262)
			       - locked <0x443023e8> (a java.lang.Object)
			       	 at java.lang.Thread.run(Thread.java:579)
				 
				 "AWT-Motif" daemon prio=1 tid=0x0x81fe840
nid=0x428a runnable [4baf0000..4baf0890]
	   at sun.awt.motif.MToolkit.run(Native Method)
	      at java.lang.Thread.run(Thread.java:579)
	      
	      "Signal Dispatcher" daemon prio=1 tid=0x0x807fc70 nid=0x4287
waiting on monitor [0..0]

"Finalizer" daemon prio=1 tid=0x0x8078cc8 nid=0x4284 waiting on monitor
[4a68f000..4a68f890]
		    at java.lang.Object.wait(Native Method)
		       - waiting on <0x442d17f0> (a java.lang.ref.ReferenceQueue$Lock)
		       	 at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:111)
			    - locked <0x442d17f0> (a java.lang.ref.ReferenceQueue$Lock)
			      at java.lang.ref.ReferenceQueue.remove(Refe
			      
			      
*** Thread dump at a point where new chat messages aren't being
    sent (but are received), and the file roller just keeps rolling
    (fixed, needed to synchronize on outputstream): (deleted thread
    dumps)
    
	       
--- Packet dumps:

HL 1.8.5 "View file" request:

Dumping payload for incoming packet:
 0 cc  0  2  0  2  0 c9  .?.....?
 0  b 61 63 63 6f 75 6e  ..accoun
74 2e 67 69 66  0 ca  0  t.gif.?.
10  0  1  0  0  b 53 63  ......Sc
72 65 65 6e 73 68 6f 74  reenshot
73                       s
HLServerDispatcher.dispatch: got packet Packet[PacketHeader[cls = 0x0, id = 0xca, trans = 6, isError = 0, len = 43, len2 = 43, hc = 3], [DataComponent[type = 0xcc, data = ], DataComponent[type = 0xc9, data = account.gif], PathComponent[type = 0xca, :Screenshots]]]


HL 1.8.5 "Download file" request:

Dumping payload for incoming packet:
 0 c9  0  b 61 63 63 6f  .?..acco
75 6e 74 2e 67 69 66  0  unt.gif.
ca  0 10  0  1  0  0  b  ?.......
53 63 72 65 65 6e 73 68  Screensh
6f 74 73                 ots
HLServerDispatcher.dispatch: got packet Packet[PacketHeader[cls = 0x0, id = 0xca, trans = 8, isError = 0, len = 37, len2 = 37, hc = 2], [DataComponent[type = 0xc9, data = account.gif], PathComponent[type = 0xca, :Screenshots]]]


Difference: 

"View file" sends an extra 2 byte 0xcc object
(HLProtocol.HTLC_DATA_RESUME) of value 2.

