Browse Source

Import Plugins

tags/0.6.3m3^0
Shane Mc Cormack 14 years ago
parent
commit
86a7294aac
100 changed files with 11643 additions and 0 deletions
  1. 95
    0
      src/com/dmdirc/addons/audio/AudioCommand.java
  2. 128
    0
      src/com/dmdirc/addons/audio/AudioPlayer.java
  3. 67
    0
      src/com/dmdirc/addons/audio/AudioPlugin.java
  4. 85
    0
      src/com/dmdirc/addons/audio/BeepCommand.java
  5. 25
    0
      src/com/dmdirc/addons/audio/plugin.config
  6. 343
    0
      src/com/dmdirc/addons/dcc/DCC.java
  7. 136
    0
      src/com/dmdirc/addons/dcc/DCCChat.java
  8. 54
    0
      src/com/dmdirc/addons/dcc/DCCChatInterface.java
  9. 145
    0
      src/com/dmdirc/addons/dcc/DCCChatWindow.java
  10. 243
    0
      src/com/dmdirc/addons/dcc/DCCCommand.java
  11. 96
    0
      src/com/dmdirc/addons/dcc/DCCCommandParser.java
  12. 239
    0
      src/com/dmdirc/addons/dcc/DCCFrame.java
  13. 611
    0
      src/com/dmdirc/addons/dcc/DCCPlugin.java
  14. 491
    0
      src/com/dmdirc/addons/dcc/DCCSend.java
  15. 54
    0
      src/com/dmdirc/addons/dcc/DCCSendInterface.java
  16. 339
    0
      src/com/dmdirc/addons/dcc/DCCSendWindow.java
  17. 86
    0
      src/com/dmdirc/addons/dcc/actions/DCCActions.java
  18. 104
    0
      src/com/dmdirc/addons/dcc/actions/DCCEvents.java
  19. 108
    0
      src/com/dmdirc/addons/dcc/kde/KDialogProcess.java
  20. 336
    0
      src/com/dmdirc/addons/dcc/kde/KFileChooser.java
  21. 101
    0
      src/com/dmdirc/addons/dcc/kde/StreamReader.java
  22. 74
    0
      src/com/dmdirc/addons/dcc/plugin.config
  23. BIN
      src/com/dmdirc/addons/dcc/res/chat-inactive.png
  24. BIN
      src/com/dmdirc/addons/dcc/res/chat.png
  25. BIN
      src/com/dmdirc/addons/dcc/res/receive-done.png
  26. BIN
      src/com/dmdirc/addons/dcc/res/receive-failed.png
  27. BIN
      src/com/dmdirc/addons/dcc/res/receive-inactive.png
  28. BIN
      src/com/dmdirc/addons/dcc/res/receive.png
  29. BIN
      src/com/dmdirc/addons/dcc/res/send-done.png
  30. BIN
      src/com/dmdirc/addons/dcc/res/send-failed.png
  31. BIN
      src/com/dmdirc/addons/dcc/res/send-inactive.png
  32. BIN
      src/com/dmdirc/addons/dcc/res/send.png
  33. 94
    0
      src/com/dmdirc/addons/dcc/res/source/chat-inactive.svg
  34. 94
    0
      src/com/dmdirc/addons/dcc/res/source/chat.svg
  35. 96
    0
      src/com/dmdirc/addons/dcc/res/source/receive-done.svg
  36. 99
    0
      src/com/dmdirc/addons/dcc/res/source/receive-failed.svg
  37. 103
    0
      src/com/dmdirc/addons/dcc/res/source/receive-inactive.svg
  38. 111
    0
      src/com/dmdirc/addons/dcc/res/source/receive.svg
  39. 96
    0
      src/com/dmdirc/addons/dcc/res/source/send-done.svg
  40. 99
    0
      src/com/dmdirc/addons/dcc/res/source/send-failed.svg
  41. 102
    0
      src/com/dmdirc/addons/dcc/res/source/send-inactive.svg
  42. 102
    0
      src/com/dmdirc/addons/dcc/res/source/send.svg
  43. 126
    0
      src/com/dmdirc/addons/dcc/res/source/transfers.svg
  44. BIN
      src/com/dmdirc/addons/dcc/res/transfers.png
  45. 82
    0
      src/com/dmdirc/addons/dcop/DcopCommand.java
  46. 95
    0
      src/com/dmdirc/addons/dcop/DcopPlugin.java
  47. 26
    0
      src/com/dmdirc/addons/dcop/package-info.java
  48. 33
    0
      src/com/dmdirc/addons/dcop/plugin.config
  49. 90
    0
      src/com/dmdirc/addons/dns/DNSCommand.java
  50. 98
    0
      src/com/dmdirc/addons/dns/DNSPlugin.java
  51. 26
    0
      src/com/dmdirc/addons/dns/package-info.java
  52. 27
    0
      src/com/dmdirc/addons/dns/plugin.config
  53. 235
    0
      src/com/dmdirc/addons/identd/IdentClient.java
  54. 172
    0
      src/com/dmdirc/addons/identd/IdentdPlugin.java
  55. 168
    0
      src/com/dmdirc/addons/identd/IdentdServer.java
  56. 41
    0
      src/com/dmdirc/addons/identd/plugin.config
  57. 63
    0
      src/com/dmdirc/addons/lagdisplay/LagDisplayPanel.java
  58. 285
    0
      src/com/dmdirc/addons/lagdisplay/LagDisplayPlugin.java
  59. 176
    0
      src/com/dmdirc/addons/lagdisplay/PingHistoryPanel.java
  60. 93
    0
      src/com/dmdirc/addons/lagdisplay/ServerInfoDialog.java
  61. 26
    0
      src/com/dmdirc/addons/lagdisplay/package-info.java
  62. 39
    0
      src/com/dmdirc/addons/lagdisplay/plugin.config
  63. 100
    0
      src/com/dmdirc/addons/logging/HistoryWindow.java
  64. 144
    0
      src/com/dmdirc/addons/logging/LoggingCommand.java
  65. 821
    0
      src/com/dmdirc/addons/logging/LoggingPlugin.java
  66. 261
    0
      src/com/dmdirc/addons/logging/ReverseFileReader.java
  67. 26
    0
      src/com/dmdirc/addons/logging/package-info.java
  68. 49
    0
      src/com/dmdirc/addons/logging/plugin.config
  69. 117
    0
      src/com/dmdirc/addons/mediasource_dcop/AmarokSource.java
  70. 88
    0
      src/com/dmdirc/addons/mediasource_dcop/DcopMediaSourcePlugin.java
  71. 147
    0
      src/com/dmdirc/addons/mediasource_dcop/KaffeineSource.java
  72. 151
    0
      src/com/dmdirc/addons/mediasource_dcop/NoatunSource.java
  73. 31
    0
      src/com/dmdirc/addons/mediasource_dcop/plugin.config
  74. 70
    0
      src/com/dmdirc/addons/mediasource_vlc/InstructionsPanel.java
  75. 310
    0
      src/com/dmdirc/addons/mediasource_vlc/VlcMediaSourcePlugin.java
  76. 40
    0
      src/com/dmdirc/addons/mediasource_vlc/plugin.config
  77. BIN
      src/com/dmdirc/addons/mediasource_vlc/vlc.png
  78. 180
    0
      src/com/dmdirc/addons/mediasource_windows/DllSource.java
  79. 74
    0
      src/com/dmdirc/addons/mediasource_windows/MediaInfoOutput.java
  80. 158
    0
      src/com/dmdirc/addons/mediasource_windows/WindowsMediaSourcePlugin.java
  81. 91
    0
      src/com/dmdirc/addons/mediasource_windows/files/GetMediaInfo.dpr
  82. BIN
      src/com/dmdirc/addons/mediasource_windows/files/GetMediaInfo.exe
  83. BIN
      src/com/dmdirc/addons/mediasource_windows/files/itunes.dll
  84. 220
    0
      src/com/dmdirc/addons/mediasource_windows/files/itunes.dpr
  85. 22
    0
      src/com/dmdirc/addons/mediasource_windows/files/prePackage.sh
  86. BIN
      src/com/dmdirc/addons/mediasource_windows/files/winamp.dll
  87. 222
    0
      src/com/dmdirc/addons/mediasource_windows/files/winamp.dpr
  88. 34
    0
      src/com/dmdirc/addons/mediasource_windows/plugin.config
  89. 81
    0
      src/com/dmdirc/addons/nickcolours/ColourRenderer.java
  90. 183
    0
      src/com/dmdirc/addons/nickcolours/NickColourInputDialog.java
  91. 245
    0
      src/com/dmdirc/addons/nickcolours/NickColourPanel.java
  92. 269
    0
      src/com/dmdirc/addons/nickcolours/NickColourPlugin.java
  93. 26
    0
      src/com/dmdirc/addons/nickcolours/package-info.java
  94. 40
    0
      src/com/dmdirc/addons/nickcolours/plugin.config
  95. 309
    0
      src/com/dmdirc/addons/nowplaying/ConfigPanel.java
  96. 96
    0
      src/com/dmdirc/addons/nowplaying/MediaSource.java
  97. 76
    0
      src/com/dmdirc/addons/nowplaying/MediaSourceComparator.java
  98. 42
    0
      src/com/dmdirc/addons/nowplaying/MediaSourceManager.java
  99. 63
    0
      src/com/dmdirc/addons/nowplaying/MediaSourceState.java
  100. 0
    0
      src/com/dmdirc/addons/nowplaying/NowPlayingCommand.java

+ 95
- 0
src/com/dmdirc/addons/audio/AudioCommand.java View File

@@ -0,0 +1,95 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.audio;
24
+
25
+import com.dmdirc.commandparser.CommandArguments;
26
+import com.dmdirc.commandparser.CommandManager;
27
+import com.dmdirc.commandparser.commands.GlobalCommand;
28
+import com.dmdirc.ui.interfaces.InputWindow;
29
+
30
+import java.io.File;
31
+
32
+/**
33
+ * The Audio Command allows playing of audio files.
34
+ *
35
+ * @author Shane "Dataforce" Mc Cormack
36
+ */
37
+public final class AudioCommand extends GlobalCommand {
38
+
39
+    /**
40
+     * Creates a new instance of LoggingCommand.
41
+     */
42
+    public AudioCommand() {
43
+        super();
44
+        CommandManager.registerCommand(this);
45
+    }
46
+
47
+    /** {@inheritDoc} */
48
+    @Override
49
+    public void execute(final InputWindow origin, final boolean isSilent,
50
+                        final CommandArguments args) {
51
+        final String filename = args.getArgumentsAsString();
52
+        final File file = new File(filename);
53
+        if (file.exists()) {
54
+            if (AudioPlayer.isValid(file)) {
55
+                new AudioPlayer(file).play();
56
+            } else {
57
+                sendLine(origin, isSilent, FORMAT_ERROR, "Invalid file type");
58
+            }
59
+        } else {
60
+            sendLine(origin, isSilent, FORMAT_ERROR, "File does not exist");
61
+        }
62
+    }
63
+
64
+    /**
65
+     * Returns this command's name.
66
+     *
67
+     * @return The name of this command
68
+     */
69
+    @Override
70
+    public String getName() {
71
+        return "audio";
72
+    }
73
+
74
+    /**
75
+     * Returns whether or not this command should be shown in help messages.
76
+     *
77
+     * @return True iff the command should be shown, false otherwise
78
+     */
79
+    @Override
80
+    public boolean showInHelp() {
81
+        return true;
82
+    }
83
+
84
+    /**
85
+     * Returns a string representing the help message for this command.
86
+     *
87
+     * @return the help message for this command
88
+     */
89
+    @Override
90
+    public String getHelp() {
91
+        return this.getName() + " <file>";
92
+    }
93
+
94
+}
95
+

+ 128
- 0
src/com/dmdirc/addons/audio/AudioPlayer.java View File

@@ -0,0 +1,128 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.audio;
24
+
25
+import java.io.File;
26
+import java.io.IOException;
27
+import java.applet.AudioClip;
28
+import java.applet.Applet;
29
+
30
+import javax.sound.sampled.UnsupportedAudioFileException;
31
+import javax.sound.sampled.AudioSystem;
32
+
33
+import java.net.MalformedURLException;
34
+
35
+/**
36
+ * The AudioPlayer handles the playing of the audio
37
+ *
38
+ * @author Shane "Dataforce" Mc Cormack
39
+ */
40
+public final class AudioPlayer implements Runnable {
41
+
42
+    /** The AudioType enum */
43
+    public static enum AudioType {
44
+
45
+        WAV, INVALID;
46
+
47
+    }
48
+
49
+    /** The file object of the file to play */
50
+    final File myFile;
51
+
52
+    /**
53
+     * Create the AudioPlayer
54
+     *
55
+     * @param file The file to play
56
+     */
57
+    public AudioPlayer(final File file) {
58
+        myFile = file;
59
+    }
60
+
61
+    /**
62
+     * Play this AudioPlayer.
63
+     */
64
+    public void play() {
65
+        new Thread(this).start();
66
+    }
67
+
68
+    /**
69
+     * Run this AudioPlayer (Should not be invoked directly).
70
+     */
71
+    @Override
72
+    public void run() {
73
+        final AudioType type = getAudioType(myFile);
74
+        switch (type) {
75
+            case WAV:
76
+                playWav();
77
+                break;
78
+            default:
79
+                break;
80
+        }
81
+    }
82
+
83
+    /**
84
+     * Check if this File is a supported file type
85
+     *
86
+     * @param file the File to check
87
+     * @return true if playable, else false.
88
+     */
89
+    public static boolean isValid(final File file) {
90
+        final AudioType type = getAudioType(file);
91
+        return (type != AudioType.INVALID);
92
+    }
93
+
94
+    /**
95
+     * Get the AudioType of a given file
96
+     *
97
+     * @param file the File to check
98
+     * @return AudioType for this file.
99
+     */
100
+    public static AudioType getAudioType(final File file) {
101
+        AudioType type;
102
+        try {
103
+            AudioSystem.getAudioInputStream(file);
104
+            type = AudioType.WAV;
105
+        } catch (UnsupportedAudioFileException e) {
106
+            type = AudioType.INVALID;
107
+        } catch (IOException e) {
108
+            type = AudioType.INVALID;
109
+        }
110
+        return type;
111
+    }
112
+
113
+    /**
114
+     * Play the file as a wav file, using the Applet class.
115
+     * (This code seems to work better than the non-applet version, but can't play
116
+     * streams)
117
+     */
118
+    private void playWav() {
119
+        try {
120
+            final AudioClip ac = Applet.newAudioClip(myFile.toURI().toURL());
121
+            if (ac != null) {
122
+                ac.play();
123
+            }
124
+        } catch (MalformedURLException e) {
125
+        }
126
+    }
127
+
128
+}

+ 67
- 0
src/com/dmdirc/addons/audio/AudioPlugin.java View File

@@ -0,0 +1,67 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.audio;
24
+
25
+import com.dmdirc.plugins.Plugin;
26
+import com.dmdirc.commandparser.CommandManager;
27
+
28
+/**
29
+ * Adds Audio playing facility to client.
30
+ *
31
+ * @author Shane 'Dataforce' McCormack
32
+ */
33
+public final class AudioPlugin extends Plugin {
34
+
35
+    /** The AudioCommand we created */
36
+    private AudioCommand audioCommand = null;
37
+
38
+    /** The BeepCommand we created */
39
+    private BeepCommand beepCommand = null;
40
+
41
+    /**
42
+     * Creates a new instance of the Audio Plugin.
43
+     */
44
+    public AudioPlugin() {
45
+        super();
46
+    }
47
+
48
+    /**
49
+     * Called when the plugin is loaded.
50
+     */
51
+    @Override
52
+    public void onLoad() {
53
+        audioCommand = new AudioCommand();
54
+        beepCommand = new BeepCommand();
55
+    }
56
+
57
+    /**
58
+     * Called when this plugin is Unloaded
59
+     */
60
+    @Override
61
+    public void onUnload() {
62
+        CommandManager.unregisterCommand(beepCommand);
63
+        CommandManager.unregisterCommand(audioCommand);
64
+    }
65
+
66
+}
67
+

+ 85
- 0
src/com/dmdirc/addons/audio/BeepCommand.java View File

@@ -0,0 +1,85 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.audio;
24
+
25
+import com.dmdirc.commandparser.CommandArguments;
26
+import com.dmdirc.commandparser.CommandManager;
27
+import com.dmdirc.commandparser.commands.GlobalCommand;
28
+import com.dmdirc.ui.interfaces.InputWindow;
29
+
30
+import java.awt.Toolkit;
31
+
32
+/**
33
+ * The Beep Command emits a beep
34
+ *
35
+ * @author Shane "Dataforce" Mc Cormack
36
+ */
37
+public final class BeepCommand extends GlobalCommand {
38
+
39
+    /**
40
+     * Creates a new instance of BeepCommand.
41
+     */
42
+    public BeepCommand() {
43
+        super();
44
+        CommandManager.registerCommand(this);
45
+    }
46
+
47
+    /** {@inheritDoc} */
48
+    @Override
49
+    public void execute(final InputWindow origin, final boolean isSilent,
50
+                        final CommandArguments args) {
51
+        Toolkit.getDefaultToolkit().beep();
52
+    }
53
+
54
+    /**
55
+     * Returns this command's name.
56
+     *
57
+     * @return The name of this command
58
+     */
59
+    @Override
60
+    public String getName() {
61
+        return "beep";
62
+    }
63
+
64
+    /**
65
+     * Returns whether or not this command should be shown in help messages.
66
+     *
67
+     * @return True iff the command should be shown, false otherwise
68
+     */
69
+    @Override
70
+    public boolean showInHelp() {
71
+        return true;
72
+    }
73
+
74
+    /**
75
+     * Returns a string representing the help message for this command.
76
+     *
77
+     * @return the help message for this command
78
+     */
79
+    @Override
80
+    public String getHelp() {
81
+        return this.getName();
82
+    }
83
+
84
+}
85
+

+ 25
- 0
src/com/dmdirc/addons/audio/plugin.config View File

@@ -0,0 +1,25 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  updates
9
+  version
10
+
11
+metadata:
12
+  author=Shane <shane@dmdirc.com>
13
+  mainclass=com.dmdirc.addons.audio.AudioPlugin
14
+  description=Allows playing audio files
15
+  name=audio
16
+  nicename=Audio Plugin
17
+
18
+updates:
19
+  id=14
20
+
21
+version:
22
+  friendly=0.3
23
+
24
+provides:
25
+  audio command

+ 343
- 0
src/com/dmdirc/addons/dcc/DCC.java View File

@@ -0,0 +1,343 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+import java.net.Socket;
26
+import java.net.ServerSocket;
27
+import java.io.IOException;
28
+import java.util.concurrent.Semaphore;
29
+
30
+/**
31
+ * This class handles the main "grunt work" of DCC, subclasses process the data
32
+ * received by this class.
33
+ *
34
+ * @author Shane 'Dataforce' McCormack
35
+ */
36
+public abstract class DCC implements Runnable {
37
+
38
+    /** Address. */
39
+    protected long address = 0;
40
+
41
+    /** Port. */
42
+    protected int port = 0;
43
+
44
+    /** Socket used to communicate with. */
45
+    protected Socket socket;
46
+
47
+    /** The Thread in use for this. */
48
+    private volatile Thread myThread;
49
+
50
+    /** Are we already running? */
51
+    protected boolean running = false;
52
+
53
+    /** Are we a listen socket? */
54
+    protected boolean listen = false;
55
+
56
+    /**
57
+     * The current socket in use if this is a listen socket.
58
+     * This reference may be changed if and only if exactly one permit from the
59
+     * <code>serverSocketSem</code> and <code>serverListenignSem</code>
60
+     * semaphores is held by the thread doing the modification.
61
+     */
62
+    private ServerSocket serverSocket;
63
+
64
+    /**
65
+     * Semaphore to control write access to ServerSocket.
66
+     * If an object acquires a permit from the <code>serverSocketSem</code>, then
67
+     * <code>serverSocket</code> is <em>guaranteed</em> not to be externally
68
+     * modified until that permit is released, <em>unless</em> the object also
69
+     * acquires a permit from the <code>serverListeningSem</code>.
70
+     */
71
+    private final Semaphore serverSocketSem = new Semaphore(1);
72
+
73
+    /**
74
+     * Semaphore used when we're blocking waiting for connections.
75
+     * If an object acquires a permit from the <code>serverListeningSem</code>,
76
+     * then it is <em>guaranteed</em> that the {@link #run()} method is blocking
77
+     * waiting for incoming connections. In addition, it is <em>guaranteed</em>
78
+     * that the {@link #run()} method is holding the <code>serverSocketSem</code>
79
+     * permit, and it will continue holding that permit until it can reaquire
80
+     * the <code>serverListeningSem</code> permit.
81
+     */
82
+    private final Semaphore serverListeningSem = new Semaphore(0);
83
+
84
+    /**
85
+     * Creates a new instance of DCC.
86
+     */
87
+    public DCC() {
88
+        super();
89
+    }
90
+
91
+    /**
92
+     * Connect this dcc.
93
+     */
94
+    public void connect() {
95
+        try {
96
+            if (listen) {
97
+                address = 0;
98
+                port = serverSocket.getLocalPort();
99
+            } else {
100
+                // socket = new Socket(longToIP(address), port, bindIP, 0);
101
+                socket = new Socket(longToIP(address), port);
102
+                socketOpened();
103
+            }
104
+        } catch (IOException ioe) {
105
+            socketClosed();
106
+            return;
107
+        }
108
+
109
+        myThread = new Thread(this);
110
+        myThread.start();
111
+    }
112
+
113
+    /**
114
+     * Start a listen socket rather than a connect socket.
115
+     *
116
+     * @throws IOException If the listen socket can't be created
117
+     */
118
+    public void listen() throws IOException {
119
+        serverSocketSem.acquireUninterruptibly();
120
+        serverSocket = new ServerSocket(0, 1);
121
+        serverSocketSem.release();
122
+
123
+        listen = true;
124
+        connect();
125
+    }
126
+
127
+    /**
128
+     * Start a listen socket rather than a connect socket, use a port from the
129
+     * given range.
130
+     *
131
+     * @param startPort Port to try first
132
+     * @param endPort Last port to try.
133
+     * @throws IOException If no sockets were available in the given range
134
+     */
135
+    public void listen(final int startPort, final int endPort) throws IOException {
136
+        listen = true;
137
+
138
+        for (int i = startPort; i <= endPort; ++i) {
139
+            try {
140
+                serverSocketSem.acquireUninterruptibly();
141
+                serverSocket = new ServerSocket(i, 1);
142
+                serverSocketSem.release();
143
+                // Found a socket we can use!
144
+                break;
145
+            } catch (IOException ioe) {
146
+                // Try next socket.
147
+            } catch (SecurityException se) {
148
+                // Try next socket.
149
+            }
150
+        }
151
+
152
+        if (serverSocket == null) {
153
+            throw new IOException("No available sockets in range " + startPort + ":" + endPort);
154
+        } else {
155
+            connect();
156
+        }
157
+    }
158
+
159
+    /**
160
+     * This handles the socket to keep it out of the main thread
161
+     */
162
+    @Override
163
+    public void run() {
164
+        if (running) {
165
+            return;
166
+        }
167
+        running = true;
168
+        // handleSocket is implemented by sub classes, and should return false
169
+        // when the socket is closed.
170
+        Thread thisThread = Thread.currentThread();
171
+
172
+        while (myThread == thisThread) {
173
+            serverSocketSem.acquireUninterruptibly();
174
+
175
+            if (serverSocket == null) {
176
+                serverSocketSem.release();
177
+
178
+                if (!handleSocket()) {
179
+                    close();
180
+                    break;
181
+                }
182
+            } else {
183
+                try {
184
+                    serverListeningSem.release();
185
+                    socket = serverSocket.accept();
186
+                    serverSocket.close();
187
+                    socketOpened();
188
+                } catch (IOException ioe) {
189
+                    socketClosed();
190
+                    break;
191
+                } finally {
192
+                    serverListeningSem.acquireUninterruptibly();
193
+                    serverSocket = null;
194
+                    serverSocketSem.release();
195
+                }
196
+            }
197
+
198
+            // Sleep for a short period of time to reduce CPU usage.
199
+            try {
200
+                Thread.sleep(100);
201
+            } catch (InterruptedException ie) {
202
+            }
203
+        }
204
+        // Socket closed
205
+
206
+        thisThread = null;
207
+        running = false;
208
+    }
209
+
210
+    /**
211
+     * Called to close the socket
212
+     */
213
+    protected void close() {
214
+        boolean haveSLS = false;
215
+
216
+        while (!serverSocketSem.tryAcquire() && !(haveSLS = serverListeningSem.tryAcquire())) {
217
+            try {
218
+                Thread.sleep(100);
219
+            } catch (InterruptedException ex) {
220
+                // Do we care? I doubt we do! Should be unchecked damnit.
221
+            }
222
+        }
223
+
224
+        if (serverSocket != null) {
225
+            try {
226
+                if (!serverSocket.isClosed()) {
227
+                    serverSocket.close();
228
+                }
229
+            } catch (IOException ioe) {
230
+            }
231
+            serverSocket = null;
232
+        }
233
+
234
+        if (haveSLS) {
235
+            serverListeningSem.release();
236
+        } else {
237
+            serverSocketSem.release();
238
+        }
239
+
240
+        if (socket != null) {
241
+            try {
242
+                if (!socket.isClosed()) {
243
+                    socket.close();
244
+                }
245
+            } catch (IOException ioe) {
246
+            }
247
+            socketClosed();
248
+            socket = null;
249
+        }
250
+    }
251
+
252
+    /**
253
+     * Called when the socket is first opened, before any data is handled.
254
+     */
255
+    protected void socketOpened() {
256
+    }
257
+
258
+    /**
259
+     * Called when the socket is closed, before the thread terminates.
260
+     */
261
+    protected void socketClosed() {
262
+    }
263
+
264
+    /**
265
+     * Check if this socket can be written to.
266
+     *
267
+     * @return True if the socket is writable, false otehrwise
268
+     */
269
+    public boolean isWriteable() {
270
+        return false;
271
+    }
272
+
273
+    /**
274
+     * Handle the socket.
275
+     *
276
+     * @return false when socket is closed, true will cause the method to be
277
+     *         called again.
278
+     */
279
+    protected abstract boolean handleSocket();
280
+
281
+    /**
282
+     * Set the address to connect to for this DCC
283
+     *
284
+     * @param address Address as an int (Network Byte Order, as specified in the DCC CTCP)
285
+     * @param port Port to connect to
286
+     */
287
+    public void setAddress(final long address, final int port) {
288
+        this.address = address;
289
+        this.port = port;
290
+    }
291
+
292
+    /**
293
+     * Is this a listening socket
294
+     *
295
+     * @return True if this is a listening socket
296
+     */
297
+    public boolean isListenSocket() {
298
+        return listen;
299
+    }
300
+
301
+    /**
302
+     * Get the host this socket is listening on/connecting to
303
+     *
304
+     * @return The IP that this socket is listening on/connecting to.
305
+     */
306
+    public String getHost() {
307
+        return longToIP(address);
308
+    }
309
+
310
+    /**
311
+     * Get the port this socket is listening on/connecting to
312
+     *
313
+     * @return The port that this socket is listening on/connecting to.
314
+     */
315
+    public int getPort() {
316
+        return port;
317
+    }
318
+
319
+    /**
320
+     * Convert the given IP Address to a long
321
+     *
322
+     * @param ip Input IP Address
323
+     * @return ip as a long
324
+     */
325
+    public static long ipToLong(final String ip) {
326
+        final String bits[] = ip.split("\\.");
327
+        if (bits.length > 3) {
328
+            return (Long.parseLong(bits[0]) << 24) + (Long.parseLong(bits[1]) << 16) + (Long.parseLong(bits[2]) << 8) + Long.parseLong(bits[3]);
329
+        }
330
+        return 0;
331
+    }
332
+
333
+    /**
334
+     * Convert the given long to an IP Address
335
+     *
336
+     * @param in Input long
337
+     * @return long as an IP
338
+     */
339
+    public static String longToIP(final long in) {
340
+        return ((in & 0xff000000) >> 24) + "." + ((in & 0x00ff0000) >> 16) + "." + ((in & 0x0000ff00) >> 8) + "." + (in & 0x000000ff);
341
+    }
342
+
343
+}

+ 136
- 0
src/com/dmdirc/addons/dcc/DCCChat.java View File

@@ -0,0 +1,136 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+import java.io.BufferedReader;
26
+import java.io.IOException;
27
+import java.io.InputStreamReader;
28
+import java.io.PrintWriter;
29
+
30
+/**
31
+ * This class handles a DCC Chat
32
+ *
33
+ * @author Shane 'Dataforce' McCormack
34
+ */
35
+public class DCCChat extends DCC {
36
+
37
+    /** The handler for this DCCChat. */
38
+    private DCCChatInterface handler = null;
39
+
40
+    /** Used to send data out the socket. */
41
+    private PrintWriter out;
42
+
43
+    /** Used to read data from the socket. */
44
+    private BufferedReader in;
45
+
46
+    /**
47
+     * Creates a new instance of DCCChat.
48
+     */
49
+    public DCCChat() {
50
+        super();
51
+    }
52
+
53
+    /**
54
+     * Change the handler for this DCC Chat.
55
+     *
56
+     * @param handler A class implementing DCCChatInterface
57
+     */
58
+    public void setHandler(final DCCChatInterface handler) {
59
+        this.handler = handler;
60
+    }
61
+
62
+    /**
63
+     * Called when the socket is first opened, before any data is handled.
64
+     */
65
+    @Override
66
+    protected void socketOpened() {
67
+        try {
68
+            out = new PrintWriter(socket.getOutputStream(), true);
69
+            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
70
+            if (handler != null) {
71
+                handler.socketOpened(this);
72
+            }
73
+        } catch (IOException ioe) {
74
+            socketClosed();
75
+        }
76
+    }
77
+
78
+    /**
79
+     * Called when the socket is closed, before the thread terminates.
80
+     */
81
+    @Override
82
+    protected void socketClosed() {
83
+        out = null;
84
+        in = null;
85
+        if (handler != null) {
86
+            handler.socketClosed(this);
87
+        }
88
+    }
89
+
90
+    /**
91
+     * Handle the socket.
92
+     *
93
+     * @return false when socket is closed, true will cause the method to be
94
+     *         called again.
95
+     */
96
+    @Override
97
+    protected boolean handleSocket() {
98
+        if (out == null || in == null) {
99
+            return false;
100
+        }
101
+        final String inLine;
102
+        try {
103
+            inLine = in.readLine();
104
+            if (inLine == null) {
105
+                return false;
106
+            } else {
107
+                if (handler != null) {
108
+                    handler.handleChatMessage(this, inLine);
109
+                }
110
+                return true;
111
+            }
112
+        } catch (IOException e) {
113
+            return false;
114
+        }
115
+    }
116
+
117
+    /**
118
+     * Check if this socket can be written to.
119
+     */
120
+    @Override
121
+    public boolean isWriteable() {
122
+        return out != null;
123
+    }
124
+
125
+    /**
126
+     * Send a line out the socket.
127
+     *
128
+     * @param line The line to be sent
129
+     */
130
+    public void sendLine(final String line) {
131
+        if (out != null) {
132
+            out.printf("%s\r\n", line);
133
+        }
134
+    }
135
+
136
+}

+ 54
- 0
src/com/dmdirc/addons/dcc/DCCChatInterface.java View File

@@ -0,0 +1,54 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+/**
26
+ * This interfaces allows DCC Chat Windows to receive data from a DCCChat
27
+ *
28
+ * @author Shane 'Dataforce' McCormack
29
+ */
30
+public interface DCCChatInterface {
31
+
32
+    /**
33
+     * Handle a received message
34
+     *
35
+     * @param dcc The DCCChat that this message is from
36
+     * @param message The message
37
+     */
38
+    void handleChatMessage(final DCCChat dcc, final String message);
39
+
40
+    /**
41
+     * Called when the socket is closed
42
+     *
43
+     * @param dcc The DCCChat that this message is from
44
+     */
45
+    void socketClosed(final DCCChat dcc);
46
+
47
+    /**
48
+     * Called when the socket is opened
49
+     *
50
+     * @param dcc The DCCChat that this message is from
51
+     */
52
+    void socketOpened(final DCCChat dcc);
53
+
54
+}

+ 145
- 0
src/com/dmdirc/addons/dcc/DCCChatWindow.java View File

@@ -0,0 +1,145 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+import com.dmdirc.actions.ActionManager;
26
+import com.dmdirc.addons.dcc.actions.DCCActions;
27
+import com.dmdirc.Main;
28
+
29
+/**
30
+ * This class links DCC Chat objects to a window.
31
+ *
32
+ * @author Shane 'Dataforce' McCormack
33
+ */
34
+public class DCCChatWindow extends DCCFrame implements DCCChatInterface {
35
+
36
+    /** The DCCChat object we are a window for */
37
+    private final DCCChat dcc;
38
+
39
+    /** My Nickname */
40
+    private final String nickname;
41
+
42
+    /** Other Nickname */
43
+    private final String otherNickname;
44
+
45
+    /**
46
+     * Creates a new instance of DCCChatWindow with a given DCCChat object.
47
+     *
48
+     * @param plugin the DCC Plugin responsible for this window
49
+     * @param dcc The DCCChat object this window wraps around
50
+     * @param title The title of this window
51
+     * @param nick My Current Nickname
52
+     * @param targetNick Nickname of target
53
+     */
54
+    public DCCChatWindow(final DCCPlugin plugin, final DCCChat dcc, final String title, final String nick, final String targetNick) {
55
+        super(plugin, title, "dcc-chat-inactive", false);
56
+        this.dcc = dcc;
57
+        dcc.setHandler(this);
58
+        nickname = nick;
59
+        otherNickname = targetNick;
60
+
61
+        myWindow = Main.getUI().getInputWindow(this, DCCCommandParser.getDCCCommandParser());
62
+        plugin.addWindow(this);
63
+
64
+        myWindow.setTitle(title);
65
+        myWindow.open();
66
+    }
67
+
68
+    /**
69
+     * Get the DCCChat Object associated with this window
70
+     *
71
+     * @return The DCCChat Object associated with this window
72
+     */
73
+    public DCCChat getDCC() {
74
+        return dcc;
75
+    }
76
+
77
+    /**
78
+     * Sends a line of text to this container's source.
79
+     *
80
+     * @param line The line to be sent
81
+     */
82
+    @Override
83
+    public void sendLine(final String line) {
84
+        if (dcc.isWriteable()) {
85
+            final StringBuffer buff = new StringBuffer("DCCChatSelfMessage");
86
+            ActionManager.processEvent(DCCActions.DCC_CHAT_SELFMESSAGE, buff, this, line);
87
+            addLine(buff, nickname, myWindow.getTranscoder().encode(line));
88
+            dcc.sendLine(line);
89
+        } else {
90
+            final StringBuffer buff = new StringBuffer("DCCChatError");
91
+            addLine(buff, "Socket is closed.", myWindow.getTranscoder().encode(line));
92
+        }
93
+    }
94
+
95
+    /**
96
+     * Handle a received message
97
+     *
98
+     * @param dcc The DCCChat that this message is from
99
+     * @param message The message
100
+     */
101
+    @Override
102
+    public void handleChatMessage(final DCCChat dcc, final String message) {
103
+        final StringBuffer buff = new StringBuffer("DCCChatMessage");
104
+        ActionManager.processEvent(DCCActions.DCC_CHAT_MESSAGE, buff, this, otherNickname, message);
105
+        addLine(buff, otherNickname, myWindow.getTranscoder().encode(message));
106
+    }
107
+
108
+    /**
109
+     * Called when the socket is closed
110
+     *
111
+     * @param dcc The DCCChat that this message is from
112
+     */
113
+    @Override
114
+    public void socketClosed(final DCCChat dcc) {
115
+        final StringBuffer buff = new StringBuffer("DCCChatInfo");
116
+        ActionManager.processEvent(DCCActions.DCC_CHAT_SOCKETCLOSED, buff, this);
117
+        addLine(buff, "Socket closed");
118
+        if (!isWindowClosing()) {
119
+            setIcon("dcc-chat-inactive");
120
+        }
121
+    }
122
+
123
+    /**
124
+     * Called when the socket is opened
125
+     *
126
+     * @param dcc The DCCChat that this message is from
127
+     */
128
+    @Override
129
+    public void socketOpened(final DCCChat dcc) {
130
+        final StringBuffer buff = new StringBuffer("DCCChatInfo");
131
+        ActionManager.processEvent(DCCActions.DCC_CHAT_SOCKETOPENED, buff, this);
132
+        addLine(buff, "Socket opened");
133
+        setIcon("dcc-chat-active");
134
+    }
135
+
136
+    /**
137
+     * Closes this container (and it's associated frame).
138
+     */
139
+    @Override
140
+    public void windowClosing() {
141
+        super.windowClosing();
142
+        dcc.close();
143
+    }
144
+
145
+}

+ 243
- 0
src/com/dmdirc/addons/dcc/DCCCommand.java View File

@@ -0,0 +1,243 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+import com.dmdirc.Main;
26
+import com.dmdirc.Server;
27
+import com.dmdirc.actions.ActionManager;
28
+import com.dmdirc.addons.dcc.actions.DCCActions;
29
+import com.dmdirc.addons.dcc.kde.KFileChooser;
30
+import com.dmdirc.commandparser.CommandArguments;
31
+import com.dmdirc.commandparser.CommandManager;
32
+import com.dmdirc.commandparser.commands.IntelligentCommand;
33
+import com.dmdirc.commandparser.commands.ServerCommand;
34
+import com.dmdirc.config.IdentityManager;
35
+import com.dmdirc.parser.interfaces.Parser;
36
+import com.dmdirc.ui.input.AdditionalTabTargets;
37
+import com.dmdirc.ui.input.TabCompletionType;
38
+import com.dmdirc.ui.interfaces.InputWindow;
39
+
40
+import java.io.File;
41
+import java.util.List;
42
+
43
+import javax.swing.JFileChooser;
44
+import javax.swing.JFrame;
45
+import javax.swing.JOptionPane;
46
+
47
+/**
48
+ * This command allows starting dcc chats/file transfers
49
+ *
50
+ * @author Shane "Dataforce" Mc Cormack
51
+ */
52
+public final class DCCCommand extends ServerCommand implements IntelligentCommand {
53
+
54
+    /** My Plugin */
55
+    private final DCCPlugin myPlugin;
56
+
57
+    /**
58
+     * Creates a new instance of DCCCommand.
59
+     *
60
+     * @param plugin The DCC Plugin that this command belongs to
61
+     */
62
+    public DCCCommand(final DCCPlugin plugin) {
63
+        super();
64
+        myPlugin = plugin;
65
+        CommandManager.registerCommand(this);
66
+    }
67
+
68
+    /** {@inheritDoc} */
69
+    @Override
70
+    public void execute(final InputWindow origin, final Server server,
71
+                        final boolean isSilent, final CommandArguments args) {
72
+        if (args.getArguments().length > 1) {
73
+            final String type = args.getArguments()[0];
74
+            final String target = args.getArguments()[1];
75
+            final Parser parser = server.getParser();
76
+            final String myNickname = parser.getLocalClient().getNickname();
77
+
78
+            if (parser.isValidChannelName(target) || parser.getStringConverter().equalsIgnoreCase(target, myNickname)) {
79
+                final Thread errorThread = new Thread(new Runnable() {
80
+
81
+                    /** {@inheritDoc} */
82
+                    @Override
83
+                    public void run() {
84
+                        if (parser.getStringConverter().equalsIgnoreCase(target, myNickname)) {
85
+                            JOptionPane.showMessageDialog(null, "You can't DCC yourself.", "DCC Error", JOptionPane.ERROR_MESSAGE);
86
+                        } else {
87
+                            JOptionPane.showMessageDialog(null, "You can't DCC a channel.", "DCC Error", JOptionPane.ERROR_MESSAGE);
88
+                        }
89
+                    }
90
+
91
+                });
92
+                errorThread.start();
93
+                return;
94
+            }
95
+            if (type.equalsIgnoreCase("chat")) {
96
+                final DCCChat chat = new DCCChat();
97
+                if (myPlugin.listen(chat)) {
98
+                    final DCCChatWindow window = new DCCChatWindow(myPlugin, chat, "*Chat: " + target, myNickname, target);
99
+
100
+                    parser.sendCTCP(target, "DCC", "CHAT chat " + DCC.ipToLong(myPlugin.getListenIP(parser)) + " " + chat.getPort());
101
+
102
+                    ActionManager.processEvent(DCCActions.DCC_CHAT_REQUEST_SENT, null, server, target);
103
+
104
+                    sendLine(origin, isSilent, "DCCChatStarting", target, chat.getHost(), chat.getPort());
105
+                    window.getFrame().addLine("DCCChatStarting", target, chat.getHost(), chat.getPort());
106
+                } else {
107
+                    sendLine(origin, isSilent, "DCCChatError", "Unable to start chat with " + target + " - unable to create listen socket");
108
+                }
109
+            } else if (type.equalsIgnoreCase("send")) {
110
+                sendFile(target, origin, server, isSilent, args.getArgumentsAsString(2));
111
+            } else {
112
+                sendLine(origin, isSilent, FORMAT_ERROR, "Unknown DCC Type: '" + type + "'");
113
+            }
114
+        } else {
115
+            sendLine(origin, isSilent, FORMAT_ERROR, "Syntax: dcc <type> <target> [params]");
116
+        }
117
+    }
118
+
119
+    /**
120
+     * Ask for the file to send, then start the send.
121
+     *
122
+     * @param target Person this dcc is to.
123
+     * @param origin The InputWindow this command was issued on
124
+     * @param server The server instance that this command is being executed on
125
+     * @param isSilent Whether this command is silenced or not
126
+     * @param filename The file to send
127
+     * @since 0.6.3m1
128
+     */
129
+    public void sendFile(final String target, final InputWindow origin, final Server server, final boolean isSilent, final String filename) {
130
+        // New thread to ask the user what file to send
131
+        final File givenFile = new File(filename);
132
+        final Thread dccThread = new Thread(new Runnable() {
133
+
134
+            /** {@inheritDoc} */
135
+            @Override
136
+            public void run() {
137
+                final JFileChooser jc = (givenFile.exists()) ? KFileChooser.getFileChooser(myPlugin, givenFile) : KFileChooser.getFileChooser(myPlugin);
138
+                int result;
139
+                if (!givenFile.exists() || !givenFile.isFile()) {
140
+                    jc.setDialogTitle("Send file to " + target + " - DMDirc ");
141
+                    jc.setFileSelectionMode(JFileChooser.FILES_ONLY);
142
+                    jc.setMultiSelectionEnabled(false);
143
+                    result = jc.showOpenDialog((JFrame) Main.getUI().getMainWindow());
144
+                } else {
145
+                    jc.setSelectedFile(givenFile);
146
+                    result = JFileChooser.APPROVE_OPTION;
147
+                }
148
+                if (result == JFileChooser.APPROVE_OPTION) {
149
+                    if (jc.getSelectedFile().length() == 0) {
150
+                        JOptionPane.showMessageDialog(null, "You can't send empty files over DCC.", "DCC Error", JOptionPane.ERROR_MESSAGE);
151
+                        return;
152
+                    } else if (!jc.getSelectedFile().exists()) {
153
+                        JOptionPane.showMessageDialog(null, "Invalid file specified", "DCC Error", JOptionPane.ERROR_MESSAGE);
154
+                        return;
155
+                    }
156
+                    final Parser parser = server.getParser();
157
+                    DCCSend send = new DCCSend(IdentityManager.getGlobalConfig().getOptionInt(myPlugin.getDomain(), "send.blocksize"));
158
+                    send.setTurbo(IdentityManager.getGlobalConfig().getOptionBool(myPlugin.getDomain(), "send.forceturbo"));
159
+                    send.setType(DCCSend.TransferType.SEND);
160
+
161
+                    ActionManager.processEvent(DCCActions.DCC_SEND_REQUEST_SENT, null, server, target, jc.getSelectedFile());
162
+
163
+                    sendLine(origin, isSilent, FORMAT_OUTPUT, "Starting DCC Send with: " + target);
164
+
165
+                    send.setFileName(jc.getSelectedFile().getAbsolutePath());
166
+                    send.setFileSize(jc.getSelectedFile().length());
167
+
168
+                    if (IdentityManager.getGlobalConfig().getOptionBool(myPlugin.getDomain(), "send.reverse")) {
169
+                        new DCCSendWindow(myPlugin, send, "Send: " + target, target, server);
170
+                        parser.sendCTCP(target, "DCC", "SEND \"" + jc.getSelectedFile().getName() + "\" " + DCC.ipToLong(myPlugin.getListenIP(parser)) + " 0 " + send.getFileSize() + " " + send.makeToken() + ((send.isTurbo()) ? " T" : ""));
171
+                    } else {
172
+                        if (myPlugin.listen(send)) {
173
+                            new DCCSendWindow(myPlugin, send, "*Send: " + target, target, server);
174
+                            parser.sendCTCP(target, "DCC", "SEND \"" + jc.getSelectedFile().getName() + "\" " + DCC.ipToLong(myPlugin.getListenIP(parser)) + " " + send.getPort() + " " + send.getFileSize() + ((send.isTurbo()) ? " T" : ""));
175
+                        } else {
176
+                            sendLine(origin, isSilent, "DCCSendError", "Unable to start dcc send with " + target + " - unable to create listen socket");
177
+                        }
178
+                    }
179
+                }
180
+            }
181
+
182
+        }, "openFileThread");
183
+        // Start the thread
184
+        dccThread.start();
185
+    }
186
+
187
+    /**
188
+     * Returns this command's name.
189
+     *
190
+     * @return The name of this command
191
+     */
192
+    @Override
193
+    public String getName() {
194
+        return "dcc";
195
+    }
196
+
197
+    /**
198
+     * Returns whether or not this command should be shown in help messages.
199
+     *
200
+     * @return True iff the command should be shown, false otherwise
201
+     */
202
+    @Override
203
+    public boolean showInHelp() {
204
+        return true;
205
+    }
206
+
207
+    /**
208
+     * Returns a string representing the help message for this command.
209
+     *
210
+     * @return the help message for this command
211
+     */
212
+    @Override
213
+    public String getHelp() {
214
+        return "dcc - Allows DCC";
215
+    }
216
+
217
+    /**
218
+     * Returns a list of suggestions for the specified argument, given the list
219
+     * of previous arguments.
220
+     * @param arg The argument that is being completed
221
+     * @param previousArgs The contents of the previous arguments, if any
222
+     * @return A list of suggestions for the argument
223
+     */
224
+    @Override
225
+    public AdditionalTabTargets getSuggestions(final int arg, final List<String> previousArgs) {
226
+        final AdditionalTabTargets res = new AdditionalTabTargets();
227
+
228
+        if (arg == 0) {
229
+            res.add("SEND");
230
+            res.add("CHAT");
231
+            res.excludeAll();
232
+        } else if (arg == 1) {
233
+            res.exclude(TabCompletionType.COMMAND);
234
+            res.exclude(TabCompletionType.CHANNEL);
235
+        } else {
236
+            res.excludeAll();
237
+        }
238
+
239
+        return res;
240
+    }
241
+
242
+}
243
+

+ 96
- 0
src/com/dmdirc/addons/dcc/DCCCommandParser.java View File

@@ -0,0 +1,96 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+import com.dmdirc.commandparser.CommandArguments;
26
+import com.dmdirc.commandparser.CommandManager;
27
+import com.dmdirc.commandparser.CommandType;
28
+import com.dmdirc.commandparser.commands.Command;
29
+import com.dmdirc.commandparser.commands.GlobalCommand;
30
+import com.dmdirc.commandparser.parsers.CommandParser;
31
+import com.dmdirc.ui.interfaces.InputWindow;
32
+
33
+/**
34
+ * DCC CommandParser
35
+ */
36
+public class DCCCommandParser extends CommandParser {
37
+
38
+    /** The singleton instance of the DCC command parser. */
39
+    private static DCCCommandParser me;
40
+
41
+    /** A version number for this class. */
42
+    private static final long serialVersionUID = 2009290901;
43
+
44
+    /**
45
+     * Creates a new instance of the GlobalCommandParser.
46
+     */
47
+    private DCCCommandParser() {
48
+        super();
49
+    }
50
+
51
+    /**
52
+     * Retrieves the singleton dcc command parser.
53
+     *
54
+     * @return The singleton DCCCommandParser
55
+     */
56
+    public static synchronized DCCCommandParser getDCCCommandParser() {
57
+        if (me == null) {
58
+            me = new DCCCommandParser();
59
+        }
60
+
61
+        return me;
62
+    }
63
+
64
+    /** Loads the relevant commands into the parser. */
65
+    @Override
66
+    protected void loadCommands() {
67
+        CommandManager.loadCommands(this, CommandType.TYPE_GLOBAL);
68
+    }
69
+
70
+    /**
71
+     * Executes the specified command with the given arguments.
72
+     *
73
+     * @param origin The window in which the command was typed
74
+     * @param isSilent Whether the command is being silenced or not
75
+     * @param command The command to be executed
76
+     * @param args The arguments to the command
77
+     */
78
+    @Override
79
+    protected void executeCommand(final InputWindow origin, final boolean isSilent, final Command command, final CommandArguments args) {
80
+        ((GlobalCommand) command).execute(origin, isSilent, args);
81
+    }
82
+
83
+    /**
84
+     * Called when the input was a line of text that was not a command.
85
+     * This normally means it is sent to the server/channel/user as-is, with
86
+     * no further processing.
87
+     *
88
+     * @param origin The window in which the command was typed
89
+     * @param line The line input by the user
90
+     */
91
+    @Override
92
+    protected void handleNonCommand(final InputWindow origin, final String line) {
93
+        origin.getContainer().sendLine(line);
94
+    }
95
+
96
+}

+ 239
- 0
src/com/dmdirc/addons/dcc/DCCFrame.java View File

@@ -0,0 +1,239 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+import com.dmdirc.Main;
26
+import com.dmdirc.Server;
27
+import com.dmdirc.WritableFrameContainer;
28
+import com.dmdirc.addons.ui_swing.components.frames.InputTextFrame;
29
+import com.dmdirc.addons.ui_swing.components.frames.TextFrame;
30
+import com.dmdirc.addons.ui_swing.SwingController;
31
+import com.dmdirc.addons.ui_swing.UIUtilities;
32
+import com.dmdirc.commandparser.PopupType;
33
+import com.dmdirc.commandparser.parsers.CommandParser;
34
+import com.dmdirc.commandparser.parsers.GlobalCommandParser;
35
+import com.dmdirc.config.IdentityManager;
36
+import com.dmdirc.ui.WindowManager;
37
+import com.dmdirc.ui.interfaces.InputWindow;
38
+
39
+import com.dmdirc.util.ReturnableThread;
40
+import java.awt.Container;
41
+
42
+import javax.swing.JPopupMenu;
43
+
44
+/**
45
+ * This class links DCC objects to a window.
46
+ *
47
+ * @author Shane 'Dataforce' McCormack
48
+ */
49
+public abstract class DCCFrame extends WritableFrameContainer {
50
+
51
+    /**
52
+     * Empty Frame.
53
+     */
54
+    private class EmptyFrame extends InputTextFrame {
55
+
56
+        /** A version number for this class. */
57
+        private static final long serialVersionUID = 200711271;
58
+
59
+        /**
60
+         * Creates a new instance of EmptyFrame.
61
+         *
62
+         * @param owner The frame container that owns this frame
63
+         */
64
+        public EmptyFrame(final WritableFrameContainer owner) {
65
+            super(owner, (SwingController) Main.getUI());
66
+            setTextPane(null);
67
+            pack();
68
+        }
69
+
70
+        /**
71
+         * Retrieves the command Parser for this input window.
72
+         *
73
+         * @return This window's command parser
74
+         */
75
+        @Override
76
+        public final CommandParser getCommandParser() {
77
+            return GlobalCommandParser.getGlobalCommandParser();
78
+        }
79
+
80
+        /** {@inheritDoc} */
81
+        @Override
82
+        public PopupType getNicknamePopupType() {
83
+            return null;
84
+        }
85
+
86
+        /** {@inheritDoc} */
87
+        @Override
88
+        public PopupType getChannelPopupType() {
89
+            return null;
90
+        }
91
+
92
+        /** {@inheritDoc} */
93
+        @Override
94
+        public PopupType getHyperlinkPopupType() {
95
+            return null;
96
+        }
97
+
98
+        /** {@inheritDoc} */
99
+        @Override
100
+        public PopupType getNormalPopupType() {
101
+            return null;
102
+        }
103
+
104
+        /** {@inheritDoc} */
105
+        @Override
106
+        public void addCustomPopupItems(final JPopupMenu popupMenu) {
107
+            //Add no custom popup items
108
+        }
109
+
110
+    }
111
+
112
+    /** The Window we're using. */
113
+    protected InputWindow myWindow = null;
114
+
115
+    /** The dcc plugin that owns this frame */
116
+    protected final DCCPlugin plugin;
117
+
118
+    /** The Window we're using. */
119
+    private boolean windowClosing = false;
120
+
121
+    /**
122
+     * Creates a new instance of DCCFrame with an empty window.
123
+     *
124
+     * @param plugin The DCCPlugin that owns this frame
125
+     * @param title The title of this window
126
+     * @param icon The icon to use
127
+     */
128
+    public DCCFrame(final DCCPlugin plugin, final String title, final String icon) {
129
+        this(plugin, title, icon, true);
130
+    }
131
+
132
+    /**
133
+     * Creates a new instance of DCCFrame.
134
+     *
135
+     * @param plugin The DCCPlugin that owns this frame
136
+     * @param title The title of this window
137
+     * @param defaultWindow Create default (empty) window. (non-default = chat frame)
138
+     * @param icon The icon to use
139
+     */
140
+    public DCCFrame(final DCCPlugin plugin, final String title, final String icon, final boolean defaultWindow) {
141
+        super(icon, title, IdentityManager.getGlobalConfig());
142
+        this.plugin = plugin;
143
+
144
+        if (defaultWindow) {
145
+            myWindow = UIUtilities.invokeAndWait(new ReturnableThread<EmptyFrame>() {
146
+
147
+                /** {@inheritDoc} */
148
+                @Override
149
+                public void run() {
150
+                    final EmptyFrame frame = new EmptyFrame(DCCFrame.this);
151
+                    frame.setTitle(title);
152
+                    setObject(frame);
153
+                }
154
+
155
+            });
156
+        }
157
+    }
158
+
159
+    /**
160
+     * Sends a line of text to this container's source.
161
+     *
162
+     * @param line The line to be sent
163
+     */
164
+    @Override
165
+    public void sendLine(final String line) {
166
+    }
167
+
168
+    /**
169
+     * Returns the maximum length that a line passed to sendLine() should be,
170
+     * in order to prevent it being truncated or causing protocol violations.
171
+     *
172
+     * @return The maximum line length for this container
173
+     */
174
+    @Override
175
+    public int getMaxLineLength() {
176
+        return 512;
177
+    }
178
+
179
+    /**
180
+     * Returns the internal frame associated with this object.
181
+     *
182
+     * @return The internal frame associated with this object
183
+     */
184
+    @Override
185
+    public InputWindow getFrame() {
186
+        return myWindow;
187
+    }
188
+
189
+    /**
190
+     * Returns the content pane of the internal frame associated with this object.
191
+     *
192
+     * @return The content pane of the internal frame associated with this object
193
+     */
194
+    public Container getContentPane() {
195
+        return ((TextFrame) getFrame()).getContentPane();
196
+    }
197
+
198
+    /**
199
+     * Returns the server instance associated with this container.
200
+     *
201
+     * @return the associated server connection
202
+     */
203
+    @Override
204
+    public Server getServer() {
205
+        return null;
206
+    }
207
+
208
+    /**
209
+     * Is the window closing?
210
+     *
211
+     * @return True if windowClosing has been called.
212
+     */
213
+    public final boolean isWindowClosing() {
214
+        return windowClosing;
215
+    }
216
+
217
+    /** {@inheritDoc} */
218
+    @Override
219
+    public void windowClosing() {
220
+        windowClosing = true;
221
+
222
+        // 1: Make the window non-visible
223
+        myWindow.setVisible(false);
224
+
225
+        // 2: Remove any callbacks or listeners
226
+        // 3: Trigger any actions neccessary
227
+        // 4: Trigger action for the window closing
228
+
229
+        // 5: Inform any parents that the window is closing
230
+        plugin.delWindow(this);
231
+
232
+        // 6: Remove the window from the window manager
233
+        WindowManager.removeWindow(myWindow);
234
+
235
+        // 7: Remove any references to the window and parents
236
+        myWindow = null; // NOPMD
237
+    }
238
+
239
+}

+ 611
- 0
src/com/dmdirc/addons/dcc/DCCPlugin.java View File

@@ -0,0 +1,611 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+import com.dmdirc.Main;
26
+import com.dmdirc.Server;
27
+import com.dmdirc.actions.ActionManager;
28
+import com.dmdirc.actions.CoreActionType;
29
+import com.dmdirc.actions.interfaces.ActionType;
30
+import com.dmdirc.addons.dcc.kde.KFileChooser;
31
+import com.dmdirc.addons.dcc.actions.DCCActions;
32
+import com.dmdirc.addons.ui_swing.components.frames.TextFrame;
33
+import com.dmdirc.addons.ui_swing.components.text.TextLabel;
34
+import com.dmdirc.commandparser.CommandManager;
35
+import com.dmdirc.config.Identity;
36
+import com.dmdirc.config.IdentityManager;
37
+import com.dmdirc.config.prefs.PreferencesCategory;
38
+import com.dmdirc.config.prefs.PreferencesManager;
39
+import com.dmdirc.config.prefs.PreferencesSetting;
40
+import com.dmdirc.config.prefs.PreferencesType;
41
+import com.dmdirc.interfaces.ActionListener;
42
+import com.dmdirc.logger.ErrorLevel;
43
+import com.dmdirc.logger.Logger;
44
+import com.dmdirc.parser.interfaces.ClientInfo;
45
+import com.dmdirc.parser.interfaces.Parser;
46
+import com.dmdirc.plugins.Plugin;
47
+import com.dmdirc.ui.WindowManager;
48
+
49
+import java.io.File;
50
+import java.io.IOException;
51
+import java.util.ArrayList;
52
+import java.util.List;
53
+import java.net.InetAddress;
54
+import java.net.UnknownHostException;
55
+
56
+import javax.swing.JFileChooser;
57
+import javax.swing.JFrame;
58
+import javax.swing.JOptionPane;
59
+
60
+/**
61
+ * This plugin adds DCC to dmdirc.
62
+ *
63
+ * @author Shane 'Dataforce' McCormack
64
+ */
65
+public final class DCCPlugin extends Plugin implements ActionListener {
66
+
67
+    /** The DCCCommand we created. */
68
+    private DCCCommand command;
69
+
70
+    /** Our DCC Container window. */
71
+    private DCCFrame container;
72
+
73
+    /** Child Frames. */
74
+    private final List<DCCFrame> childFrames = new ArrayList<DCCFrame>();
75
+
76
+    /**
77
+     * Creates a new instance of the DCC Plugin.
78
+     */
79
+    public DCCPlugin() {
80
+        super();
81
+    }
82
+
83
+    /**
84
+     * Ask a question, if the answer is the answer required, then recall handleProcessEvent.
85
+     *
86
+     * @param question Question to ask
87
+     * @param title Title of question dialog
88
+     * @param desiredAnswer Answer required
89
+     * @param type Actiontype to pass back
90
+     * @param format StringBuffer to pass back
91
+     * @param arguments arguments to pass back
92
+     */
93
+    public void askQuestion(final String question, final String title, final int desiredAnswer, final ActionType type, final StringBuffer format, final Object... arguments) {
94
+        // New thread to ask the question in to stop us locking the UI
95
+        final Thread questionThread = new Thread(new Runnable() {
96
+
97
+            /** {@inheritDoc} */
98
+            @Override
99
+            public void run() {
100
+                int result = JOptionPane.showConfirmDialog(null, question, title, JOptionPane.YES_NO_OPTION);
101
+                if (result == desiredAnswer) {
102
+                    handleProcessEvent(type, format, true, arguments);
103
+                }
104
+            }
105
+
106
+        }, "QuestionThread: " + title);
107
+        // Start the thread
108
+        questionThread.start();
109
+    }
110
+
111
+    /**
112
+     * Ask the location to save a file, then start the download.
113
+     *
114
+     * @param nickname Person this dcc is from.
115
+     * @param send The DCCSend to save for.
116
+     * @param parser The parser this send was received on
117
+     * @param reverse Is this a reverse dcc?
118
+     * @param sendFilename The name of the file which is being received
119
+     * @param token Token used in reverse dcc.
120
+     */
121
+    public void saveFile(final String nickname, final DCCSend send, final Parser parser, final boolean reverse, final String sendFilename, final String token) {
122
+        // New thread to ask the user where to save in to stop us locking the UI
123
+        final Thread dccThread = new Thread(new Runnable() {
124
+
125
+            /** {@inheritDoc} */
126
+            @Override
127
+            public void run() {
128
+                final JFileChooser jc = KFileChooser.getFileChooser(DCCPlugin.this, IdentityManager.getGlobalConfig().getOption(getDomain(), "receive.savelocation"));
129
+                jc.setDialogTitle("Save " + sendFilename + " As - DMDirc");
130
+                jc.setFileSelectionMode(JFileChooser.FILES_ONLY);
131
+                jc.setMultiSelectionEnabled(false);
132
+                jc.setSelectedFile(new File(send.getFileName()));
133
+                int result;
134
+                if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept")) {
135
+                    result = JFileChooser.APPROVE_OPTION;
136
+                } else {
137
+                    result = jc.showSaveDialog((JFrame) Main.getUI().getMainWindow());
138
+                }
139
+                if (result == JFileChooser.APPROVE_OPTION) {
140
+                    send.setFileName(jc.getSelectedFile().getPath());
141
+                    boolean resume = false;
142
+                    if (jc.getSelectedFile().exists()) {
143
+                        if (send.getFileSize() > -1 && send.getFileSize() <= jc.getSelectedFile().length()) {
144
+                            if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept")) {
145
+                                return;
146
+                            } else {
147
+                                JOptionPane.showMessageDialog((JFrame) Main.getUI().getMainWindow(), "This file has already been completed, or is longer than the file you are receiving.\nPlease choose a different file.", "Problem with selected file", JOptionPane.ERROR_MESSAGE);
148
+                                saveFile(nickname, send, parser, reverse, sendFilename, token);
149
+                                return;
150
+                            }
151
+                        } else {
152
+                            if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept")) {
153
+                                resume = true;
154
+                            } else {
155
+                                result = JOptionPane.showConfirmDialog((JFrame) Main.getUI().getMainWindow(), "This file exists already, do you want to resume an exisiting download?", "Resume Download?", JOptionPane.YES_NO_OPTION);
156
+                                resume = (result == JOptionPane.YES_OPTION);
157
+                            }
158
+                        }
159
+                    }
160
+                    if (reverse && !token.isEmpty()) {
161
+                        new DCCSendWindow(DCCPlugin.this, send, "*Receive: " + nickname, nickname, null);
162
+                        send.setToken(token);
163
+                        if (resume) {
164
+                            if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.reverse.sendtoken")) {
165
+                                parser.sendCTCP(nickname, "DCC", "RESUME " + sendFilename + " 0 " + jc.getSelectedFile().length() + " " + token);
166
+                            } else {
167
+                                parser.sendCTCP(nickname, "DCC", "RESUME " + sendFilename + " 0 " + jc.getSelectedFile().length());
168
+                            }
169
+                        } else {
170
+                            if (listen(send)) {
171
+                                parser.sendCTCP(nickname, "DCC", "SEND " + sendFilename + " " + DCC.ipToLong(getListenIP(parser)) + " " + send.getPort() + " " + send.getFileSize() + " " + token);
172
+                            } else {
173
+                                // Listen failed.
174
+                            }
175
+                        }
176
+                    } else {
177
+                        new DCCSendWindow(DCCPlugin.this, send, "Receive: " + nickname, nickname, null);
178
+                        if (resume) {
179
+                            parser.sendCTCP(nickname, "DCC", "RESUME " + sendFilename + " " + send.getPort() + " " + jc.getSelectedFile().length());
180
+                        } else {
181
+                            send.connect();
182
+                        }
183
+                    }
184
+                }
185
+            }
186
+
187
+        }, "saveFileThread: " + sendFilename);
188
+        // Start the thread
189
+        dccThread.start();
190
+    }
191
+
192
+    /**
193
+     * Process an event of the specified type.
194
+     *
195
+     * @param type The type of the event to process
196
+     * @param format Format of messages that are about to be sent. (May be null)
197
+     * @param arguments The arguments for the event
198
+     */
199
+    @Override
200
+    public void processEvent(final ActionType type, final StringBuffer format, final Object... arguments) {
201
+        handleProcessEvent(type, format, false, arguments);
202
+    }
203
+
204
+    /**
205
+     * Make the given DCC start listening.
206
+     * This will either call dcc.listen() or dcc.listen(startPort, endPort)
207
+     * depending on config.
208
+     *
209
+     * @param dcc DCC to start listening.
210
+     * @return True if Socket was opened.
211
+     */
212
+    protected boolean listen(final DCC dcc) {
213
+
214
+        final boolean usePortRange = IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "firewall.ports.usePortRange");
215
+        final int startPort = IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "firewall.ports.startPort");
216
+        final int endPort = IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "firewall.ports.endPort");
217
+
218
+        try {
219
+            if (usePortRange) {
220
+                dcc.listen(startPort, endPort);
221
+            } else {
222
+                dcc.listen();
223
+            }
224
+            return true;
225
+        } catch (IOException ioe) {
226
+            return false;
227
+        }
228
+    }
229
+
230
+    /**
231
+     * Process an event of the specified type.
232
+     *
233
+     * @param type The type of the event to process
234
+     * @param format Format of messages that are about to be sent. (May be null)
235
+     * @param dontAsk Don't ask any questions, assume yes.
236
+     * @param arguments The arguments for the event
237
+     */
238
+    public void handleProcessEvent(final ActionType type, final StringBuffer format, final boolean dontAsk, final Object... arguments) {
239
+        if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "receive.autoaccept") && !dontAsk) {
240
+            handleProcessEvent(type, format, true, arguments);
241
+            return;
242
+        }
243
+
244
+        if (type == CoreActionType.SERVER_CTCP) {
245
+            final String ctcpType = (String) arguments[2];
246
+            final String[] ctcpData = ((String) arguments[3]).split(" ");
247
+            if (ctcpType.equalsIgnoreCase("DCC")) {
248
+                if (ctcpData[0].equalsIgnoreCase("chat") && ctcpData.length > 3) {
249
+                    final String nickname = ((ClientInfo) arguments[1]).getNickname();
250
+                    if (dontAsk) {
251
+                        final DCCChat chat = new DCCChat();
252
+                        try {
253
+                            chat.setAddress(Long.parseLong(ctcpData[2]), Integer.parseInt(ctcpData[3]));
254
+                        } catch (NumberFormatException nfe) {
255
+                            return;
256
+                        }
257
+                        final String myNickname = ((Server) arguments[0]).getParser().getLocalClient().getNickname();
258
+                        final DCCFrame f = new DCCChatWindow(this, chat, "Chat: " + nickname, myNickname, nickname);
259
+                        f.getFrame().addLine("DCCChatStarting", nickname, chat.getHost(), chat.getPort());
260
+                        chat.connect();
261
+                    } else {
262
+                        ActionManager.processEvent(DCCActions.DCC_CHAT_REQUEST, null, ((Server) arguments[0]), nickname);
263
+                        askQuestion("User " + nickname + " on " + ((Server) arguments[0]).toString() + " would like to start a DCC Chat with you.\n\nDo you want to continue?", "DCC Chat Request", JOptionPane.YES_OPTION, type, format, arguments);
264
+                        return;
265
+                    }
266
+                } else if (ctcpData[0].equalsIgnoreCase("send") && ctcpData.length > 3) {
267
+                    final String nickname = ((ClientInfo) arguments[1]).getNickname();
268
+                    final String filename;
269
+                    String tmpFilename;
270
+                    // Clients tend to put files with spaces in the name in "" so lets look for that.
271
+                    final StringBuilder filenameBits = new StringBuilder();
272
+                    int i;
273
+                    final boolean quoted = ctcpData[1].startsWith("\"");
274
+                    if (quoted) {
275
+                        for (i = 1; i < ctcpData.length; i++) {
276
+                            String bit = ctcpData[i];
277
+                            if (i == 1) {
278
+                                bit = bit.substring(1);
279
+                            }
280
+                            if (bit.endsWith("\"")) {
281
+                                filenameBits.append(" " + bit.substring(0, bit.length() - 1));
282
+                                break;
283
+                            } else {
284
+                                filenameBits.append(" " + bit);
285
+                            }
286
+                        }
287
+                        tmpFilename = filenameBits.toString().trim();
288
+                    } else {
289
+                        tmpFilename = ctcpData[1];
290
+                        i = 1;
291
+                    }
292
+
293
+                    // Try to remove path names if sent.
294
+                    // Change file separatorChar from other OSs first
295
+                    if (File.separatorChar == '/') {
296
+                        tmpFilename = tmpFilename.replace('\\', File.separatorChar);
297
+                    } else {
298
+                        tmpFilename = tmpFilename.replace('/', File.separatorChar);
299
+                    }
300
+                    // Then get just the name of the file.
301
+                    filename = (new File(tmpFilename)).getName();
302
+
303
+                    final String ip = ctcpData[++i];
304
+                    final String port = ctcpData[++i];
305
+                    long size;
306
+                    if (ctcpData.length + 1 > i) {
307
+                        try {
308
+                            size = Integer.parseInt(ctcpData[++i]);
309
+                        } catch (NumberFormatException nfe) {
310
+                            size = -1;
311
+                        }
312
+                    } else {
313
+                        size = -1;
314
+                    }
315
+                    final String token = (ctcpData.length - 1 > i && !ctcpData[i + 1].equals("T")) ? ctcpData[++i] : "";
316
+
317
+                    // Ignore incorrect ports, or non-numeric IP/Port
318
+                    try {
319
+                        int portInt = Integer.parseInt(port);
320
+                        if (portInt > 65535 || portInt < 0) {
321
+                            return;
322
+                        }
323
+                        Long.parseLong(ip);
324
+                    } catch (NumberFormatException nfe) {
325
+                        return;
326
+                    }
327
+
328
+                    DCCSend send = DCCSend.findByToken(token);
329
+
330
+                    if (send == null && !dontAsk) {
331
+                        if (!token.isEmpty() && !port.equals("0")) {
332
+                            // This is a reverse DCC Send that we no longer care about.
333
+                            return;
334
+                        } else {
335
+                            ActionManager.processEvent(DCCActions.DCC_SEND_REQUEST, null, ((Server) arguments[0]), nickname, filename);
336
+                            askQuestion("User " + nickname + " on " + ((Server) arguments[0]).toString() + " would like to send you a file over DCC.\n\nFile: " + filename + "\n\nDo you want to continue?", "DCC Send Request", JOptionPane.YES_OPTION, type, format, arguments);
337
+                            return;
338
+                        }
339
+                    } else {
340
+                        final boolean newSend = send == null;
341
+                        if (newSend) {
342
+                            send = new DCCSend(IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "send.blocksize"));
343
+                            send.setTurbo(IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "send.forceturbo"));
344
+                        }
345
+                        try {
346
+                            send.setAddress(Long.parseLong(ip), Integer.parseInt(port));
347
+                        } catch (NumberFormatException nfe) {
348
+                            return;
349
+                        }
350
+                        if (newSend) {
351
+                            send.setFileName(filename);
352
+                            send.setFileSize(size);
353
+                            saveFile(nickname, send, ((Server) arguments[0]).getParser(), "0".equals(port), (quoted) ? "\"" + filename + "\"" : filename, token);
354
+                        } else {
355
+                            send.connect();
356
+                        }
357
+                    }
358
+                } else if ((ctcpData[0].equalsIgnoreCase("resume") || ctcpData[0].equalsIgnoreCase("accept")) && ctcpData.length > 2) {
359
+
360
+                    final String filename;
361
+                    // Clients tend to put files with spaces in the name in "" so lets look for that.
362
+                    final StringBuilder filenameBits = new StringBuilder();
363
+                    int i;
364
+                    final boolean quoted = ctcpData[1].startsWith("\"");
365
+                    if (quoted) {
366
+                        for (i = 1; i < ctcpData.length; i++) {
367
+                            String bit = ctcpData[i];
368
+                            if (i == 1) {
369
+                                bit = bit.substring(1);
370
+                            }
371
+                            if (bit.endsWith("\"")) {
372
+                                filenameBits.append(" " + bit.substring(0, bit.length() - 1));
373
+                                break;
374
+                            } else {
375
+                                filenameBits.append(" " + bit);
376
+                            }
377
+                        }
378
+                        filename = filenameBits.toString().trim();
379
+                    } else {
380
+                        filename = ctcpData[1];
381
+                        i = 1;
382
+                    }
383
+
384
+                    try {
385
+                        final int port = Integer.parseInt(ctcpData[++i]);
386
+                        final int position = Integer.parseInt(ctcpData[++i]);
387
+                        final String token = (ctcpData.length - 1 > i) ? " " + ctcpData[++i] : "";
388
+
389
+                        // Now look for a dcc that matches.
390
+                        for (DCCSend send : DCCSend.getSends()) {
391
+                            if (send.port == port && (new File(send.getFileName())).getName().equalsIgnoreCase(filename)) {
392
+                                if ((!token.isEmpty() && !send.getToken().isEmpty()) && (!token.equals(send.getToken()))) {
393
+                                    continue;
394
+                                }
395
+                                final Parser parser = ((Server) arguments[0]).getParser();
396
+                                final String nickname = ((ClientInfo) arguments[1]).getNickname();
397
+                                if (ctcpData[0].equalsIgnoreCase("resume")) {
398
+                                    parser.sendCTCP(nickname, "DCC", "ACCEPT " + ((quoted) ? "\"" + filename + "\"" : filename) + " " + port + " " + send.setFileStart(position) + token);
399
+                                } else {
400
+                                    send.setFileStart(position);
401
+                                    if (port == 0) {
402
+                                        // Reverse dcc
403
+                                        if (listen(send)) {
404
+                                            if (send.getToken().isEmpty()) {
405
+                                                parser.sendCTCP(nickname, "DCC", "SEND " + ((quoted) ? "\"" + filename + "\"" : filename) + " " + DCC.ipToLong(send.getHost()) + " " + send.getPort() + " " + send.getFileSize());
406
+                                            } else {
407
+                                                parser.sendCTCP(nickname, "DCC", "SEND " + ((quoted) ? "\"" + filename + "\"" : filename) + " " + DCC.ipToLong(send.getHost()) + " " + send.getPort() + " " + send.getFileSize() + " " + send.getToken());
408
+                                            }
409
+                                        } else {
410
+                                            // Listen failed.
411
+                                        }
412
+                                    } else {
413
+                                        send.connect();
414
+                                    }
415
+                                }
416
+                            }
417
+                        }
418
+                    } catch (NumberFormatException nfe) {
419
+                    }
420
+                }
421
+            }
422
+        }
423
+    }
424
+
425
+    /**
426
+     * Create the container window.
427
+     */
428
+    protected void createContainer() {
429
+        container = new DCCFrame(this, "DCCs", "dcc") {
430
+        };
431
+        final TextLabel label = new TextLabel("This is a placeholder window to group DCCs together.");
432
+        label.setText(label.getText() + "\n\nClosing this window will close all the active DCCs");
433
+        ((TextFrame) container.getFrame()).getContentPane().add(label);
434
+        WindowManager.addWindow(container.getFrame());
435
+        container.getFrame().open();
436
+    }
437
+
438
+    /**
439
+     * Add a window to the container window.
440
+     *
441
+     * @param window Window to remove
442
+     */
443
+    protected synchronized void addWindow(final DCCFrame window) {
444
+        if (window == container) {
445
+            return;
446
+        }
447
+        if (container == null) {
448
+            createContainer();
449
+        }
450
+
451
+        WindowManager.addWindow(container.getFrame(), window.getFrame());
452
+        childFrames.add(window);
453
+        window.getFrame().open();
454
+    }
455
+
456
+    /**
457
+     * Remove a window from the container window.
458
+     *
459
+     * @param window Window to remove
460
+     */
461
+    protected synchronized void delWindow(final DCCFrame window) {
462
+        if (container == null) {
463
+            return;
464
+        }
465
+        if (window == container) {
466
+            container = null;
467
+            for (DCCFrame win : childFrames) {
468
+                if (win != window) {
469
+                    win.close();
470
+                }
471
+            }
472
+            childFrames.clear();
473
+        } else {
474
+            childFrames.remove(window);
475
+            if (childFrames.isEmpty()) {
476
+                container.close();
477
+                container = null;
478
+            }
479
+        }
480
+    }
481
+
482
+    /** {@inheritDoc} */
483
+    @Override
484
+    public void domainUpdated() {
485
+        final Identity defaults = IdentityManager.getAddonIdentity();
486
+
487
+        defaults.setOption(getDomain(), "receive.savelocation",
488
+                Main.getConfigDir() + "downloads" + System.getProperty("file.separator"));
489
+    }
490
+
491
+    /**
492
+     * Called when the plugin is loaded.
493
+     */
494
+    @Override
495
+    public void onLoad() {
496
+        final File dir = new File(IdentityManager.getGlobalConfig().getOption(getDomain(), "receive.savelocation"));
497
+        if (dir.exists()) {
498
+            if (!dir.isDirectory()) {
499
+                Logger.userError(ErrorLevel.LOW, "Unable to create download dir (file exists instead)");
500
+            }
501
+        } else {
502
+            try {
503
+                dir.mkdirs();
504
+                dir.createNewFile();
505
+            } catch (IOException ex) {
506
+                Logger.userError(ErrorLevel.LOW, "Unable to create download dir");
507
+            }
508
+        }
509
+
510
+        command = new DCCCommand(this);
511
+        ActionManager.registerActionTypes(DCCActions.values());
512
+        ActionManager.addListener(this, CoreActionType.SERVER_CTCP);
513
+    }
514
+
515
+    /**
516
+     * Called when this plugin is Unloaded.
517
+     */
518
+    @Override
519
+    public synchronized void onUnload() {
520
+        CommandManager.unregisterCommand(command);
521
+        ActionManager.removeListener(this);
522
+        if (container != null) {
523
+            container.close();
524
+        }
525
+    }
526
+
527
+    /**
528
+     * Get the IP Address we should send as our listening IP.
529
+     *
530
+     * @return The IP Address we should send as our listening IP.
531
+     */
532
+    public String getListenIP() {
533
+        return getListenIP(null);
534
+    }
535
+
536
+    /**
537
+     * Get the IP Address we should send as our listening IP.
538
+     *
539
+     * @param parser Parser the IRC Parser where this dcc is initiated
540
+     * @return The IP Address we should send as our listening IP.
541
+     */
542
+    public String getListenIP(final Parser parser) {
543
+        final String configIP = IdentityManager.getGlobalConfig().getOption(getDomain(), "firewall.ip");
544
+        if (!configIP.isEmpty()) {
545
+            return configIP;
546
+        } else if (parser != null) {
547
+            final String myHost = parser.getLocalClient().getHostname();
548
+            if (!myHost.isEmpty()) {
549
+                try {
550
+                    return InetAddress.getByName(myHost).getHostAddress();
551
+                } catch (UnknownHostException e) { /* Will return default host below */ }
552
+            }
553
+        }
554
+        try {
555
+            return InetAddress.getLocalHost().getHostAddress();
556
+        } catch (UnknownHostException e) {
557
+            // This is almost certainly not what we want, but we can't work out
558
+            // the right one.
559
+            return "127.0.0.1";
560
+        }
561
+    }
562
+
563
+    /** {@inheritDoc} */
564
+    @Override
565
+    public void showConfig(final PreferencesManager manager) {
566
+        final PreferencesCategory general = new PreferencesCategory("DCC", "", "category-dcc");
567
+        final PreferencesCategory firewall = new PreferencesCategory("Firewall", "");
568
+        final PreferencesCategory sending = new PreferencesCategory("Sending", "");
569
+        final PreferencesCategory receiving = new PreferencesCategory("Receiving", "");
570
+
571
+        manager.getCategory("Plugins").addSubCategory(general.setInlineAfter());
572
+        general.addSubCategory(firewall.setInline());
573
+        general.addSubCategory(sending.setInline());
574
+        general.addSubCategory(receiving.setInline());
575
+
576
+        firewall.addSetting(new PreferencesSetting(PreferencesType.TEXT,
577
+                getDomain(), "firewall.ip", "Forced IP",
578
+                "What IP should be sent as our IP (Blank = work it out)"));
579
+        firewall.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
580
+                getDomain(), "firewall.ports.usePortRange", "Use Port Range",
581
+                "Useful if you have a firewall that only forwards specific ports"));
582
+        firewall.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
583
+                getDomain(), "firewall.ports.startPort", "Start Port",
584
+                "Port to try to listen on first"));
585
+        firewall.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
586
+                getDomain(), "firewall.ports.endPort", "End Port",
587
+                "Port to try to listen on last"));
588
+        receiving.addSetting(new PreferencesSetting(PreferencesType.TEXT,
589
+                getDomain(), "receive.savelocation", "Default save location",
590
+                "Where the save as window defaults to?"));
591
+        sending.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
592
+                getDomain(), "send.reverse", "Reverse DCC",
593
+                "With reverse DCC, the sender connects rather than " +
594
+                "listens like normal dcc"));
595
+        sending.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
596
+                getDomain(), "send.forceturbo", "Use Turbo DCC",
597
+                "Turbo DCC doesn't wait for ack packets. this is " +
598
+                "faster but not always supported."));
599
+        receiving.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
600
+                getDomain(), "receive.reverse.sendtoken",
601
+                "Send token in reverse receive",
602
+                "If you have problems with reverse dcc receive resume," +
603
+                " try toggling this."));
604
+        general.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
605
+                getDomain(), "send.blocksize", "Blocksize to use for DCC",
606
+                "Change the block size for send/receive, this can " +
607
+                "sometimes speed up transfers."));
608
+    }
609
+
610
+}
611
+

+ 491
- 0
src/com/dmdirc/addons/dcc/DCCSend.java View File

@@ -0,0 +1,491 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+import java.io.DataInputStream;
26
+import java.io.DataOutputStream;
27
+import java.io.File;
28
+import java.io.FileInputStream;
29
+import java.io.FileOutputStream;
30
+import java.io.IOException;
31
+import java.io.FileNotFoundException;
32
+import java.util.ArrayList;
33
+import java.util.List;
34
+
35
+/**
36
+ * This class handles a DCC Send.
37
+ *
38
+ * @author Shane 'Dataforce' McCormack
39
+ */
40
+public class DCCSend extends DCC {
41
+
42
+    /** List of active sends. */
43
+    private static final List<DCCSend> SENDS = new ArrayList<DCCSend>();
44
+
45
+    /** File Transfer Types. */
46
+    public enum TransferType {
47
+
48
+        SEND, RECEIVE;
49
+
50
+    }
51
+
52
+    /** The File transfer type for this file. */
53
+    private TransferType transferType = TransferType.RECEIVE;
54
+
55
+    /** The handler for this DCCSend. */
56
+    private DCCSendInterface handler;
57
+
58
+    /** Used to send data out the socket. */
59
+    private DataOutputStream out;
60
+
61
+    /** Used to read data from the socket. */
62
+    private DataInputStream in;
63
+
64
+    /** File we are using. */
65
+    private File transferFile;
66
+
67
+    /** Used to write data to the file. */
68
+    private DataOutputStream fileOut;
69
+
70
+    /** Used to read data from the file. */
71
+    private DataInputStream fileIn;
72
+
73
+    /** Where are we starting from? */
74
+    private int startpos;
75
+
76
+    /** How big is this file? */
77
+    private long size = -1;
78
+
79
+    /** How much of this file have we read so far? */
80
+    private long readSize;
81
+
82
+    /** What is the name of the file? */
83
+    private String filename = "";
84
+
85
+    /** What is the token for this send? */
86
+    private String token = "";
87
+
88
+    /** Block Size. */
89
+    private final int blockSize;
90
+
91
+    /** Is this a turbo dcc? */
92
+    private boolean turbo = false;
93
+
94
+    /** Creates a new instance of DCCSend with a default block size. */
95
+    public DCCSend() {
96
+        this(1024);
97
+    }
98
+
99
+    /**
100
+     * Creates a new instance of DCCSend.
101
+     *
102
+     * @param blockSize Block size to use
103
+     */
104
+    public DCCSend(final int blockSize) {
105
+        super();
106
+        this.blockSize = blockSize;
107
+        synchronized (SENDS) {
108
+            SENDS.add(this);
109
+        }
110
+    }
111
+
112
+    /**
113
+     * Reset this send to be used again (eg a resend).
114
+     */
115
+    public void reset() {
116
+        close();
117
+        setFileName(filename);
118
+        setFileStart(startpos);
119
+    }
120
+
121
+    /**
122
+     * Get a copy of the list of active sends.
123
+     *
124
+     * @return A copy of the list of active sends.
125
+     */
126
+    public static List<DCCSend> getSends() {
127
+        synchronized (SENDS) {
128
+            return new ArrayList<DCCSend>(SENDS);
129
+        }
130
+    }
131
+
132
+    /**
133
+     * Called to remove this object from the sends list.
134
+     */
135
+    public void removeFromSends() {
136
+        synchronized (SENDS) {
137
+            SENDS.remove(this);
138
+        }
139
+    }
140
+
141
+    /**
142
+     * Set the filename of this file
143
+     *
144
+     * @param filename Filename
145
+     */
146
+    public void setFileName(final String filename) {
147
+        this.filename = filename;
148
+        if (transferType == TransferType.SEND) {
149
+            transferFile = new File(filename);
150
+            try {
151
+                fileIn = new DataInputStream(new FileInputStream(transferFile.getAbsolutePath()));
152
+            } catch (FileNotFoundException e) {
153
+                fileIn = null;
154
+            } catch (SecurityException e) {
155
+                fileIn = null;
156
+            }
157
+        }
158
+    }
159
+
160
+    /**
161
+     * Get the filename of this file
162
+     *
163
+     * @return Filename
164
+     */
165
+    public String getFileName() {
166
+        return filename;
167
+    }
168
+
169
+    /**
170
+     * Get the filename of this file, without the path
171
+     *
172
+     * @return Filename without path
173
+     */
174
+    public String getShortFileName() {
175
+        return (new File(filename)).getName();
176
+    }
177
+
178
+    /**
179
+     * Set dcc Type.
180
+     *
181
+     * @param type Type of DCC Send this is.
182
+     */
183
+    public void setType(final TransferType type) {
184
+        this.transferType = type;
185
+    }
186
+
187
+    /**
188
+     * Get dcc Type.
189
+     *
190
+     * @return Type of DCC Send this is.
191
+     */
192
+    public TransferType getType() {
193
+        return transferType;
194
+    }
195
+
196
+    /**
197
+     * Set turbo mode on/off.
198
+     * Turbo mode doesn't wait for ack packets. Only relevent when sending.
199
+     *
200
+     * @param turbo True for turbo dcc, else false
201
+     */
202
+    public void setTurbo(final boolean turbo) {
203
+        this.turbo = turbo;
204
+    }
205
+
206
+    /**
207
+     * Is turbo mode on/off.
208
+     * Turbo mode doesn't wait for ack packets. Only relevent when sending.
209
+     *
210
+     * @return True for turbo dcc, else false
211
+     */
212
+    public boolean isTurbo() {
213
+        return turbo;
214
+    }
215
+
216
+    /**
217
+     * Set the Token for this send
218
+     *
219
+     * @param token Token for this send
220
+     */
221
+    public void setToken(final String token) {
222
+        this.token = token;
223
+    }
224
+
225
+    /**
226
+     * Get the Token for this send
227
+     *
228
+     * @return Token for this send
229
+     */
230
+    public String getToken() {
231
+        return token;
232
+    }
233
+
234
+    /**
235
+     * Make a Token for this send.
236
+     * This token will be unique compared to all the other known sends
237
+     *
238
+     * @return The Token for this send.
239
+     */
240
+    public String makeToken() {
241
+        String myToken = "";
242
+        boolean unique = true;
243
+        do {
244
+            myToken = Integer.toString(Math.abs((myToken + filename).hashCode()));
245
+            unique = (findByToken(myToken) == null);
246
+        } while (!unique);
247
+        setToken(myToken);
248
+        return myToken;
249
+    }
250
+
251
+    /**
252
+     * Find a send based on a given token.
253
+     *
254
+     * @param token Token to look for. (case sensitive)
255
+     * @return The first DCCSend that matches the given token.
256
+     *         null if none match, or token is "" or null.
257
+     */
258
+    public static DCCSend findByToken(final String token) {
259
+        if (token == null || token.isEmpty()) {
260
+            return null;
261
+        }
262
+        for (DCCSend send : getSends()) {
263
+            if (send.getToken().equals(token)) {
264
+                return send;
265
+            }
266
+        }
267
+        return null;
268
+    }
269
+
270
+    /**
271
+     * Set the size of the file
272
+     *
273
+     * @param size File size
274
+     */
275
+    public void setFileSize(final long size) {
276
+        this.size = size;
277
+    }
278
+
279
+    /**
280
+     * Get the expected size of the file
281
+     *
282
+     * @return The expected File size (-1 if unknown)
283
+     */
284
+    public long getFileSize() {
285
+        return size;
286
+    }
287
+
288
+    /**
289
+     * Set the starting position of the file
290
+     *
291
+     * @param startpos Starting position
292
+     * @return -1 if fileIn is null or if dcc receive, else the result of fileIn.skipBytes()
293
+     */
294
+    public int setFileStart(final int startpos) {
295
+        this.startpos = startpos;
296
+        if (transferType == TransferType.SEND && fileIn != null) {
297
+            try {
298
+                this.startpos = fileIn.skipBytes(startpos);
299
+                readSize = startpos;
300
+                return this.startpos;
301
+            } catch (IOException ioe) {
302
+            }
303
+        }
304
+        return -1;
305
+    }
306
+
307
+    /**
308
+     * Get the starting position of the file
309
+     *
310
+     * @return starting position of file.
311
+     */
312
+    public int getFileStart() {
313
+        return this.startpos;
314
+    }
315
+
316
+    /**
317
+     * Change the handler for this DCC Send
318
+     *
319
+     * @param handler A class implementing DCCSendInterface
320
+     */
321
+    public void setHandler(final DCCSendInterface handler) {
322
+        this.handler = handler;
323
+    }
324
+
325
+    /**
326
+     * Called when the socket is first opened, before any data is handled.
327
+     */
328
+    @Override
329
+    protected void socketOpened() {
330
+        try {
331
+            transferFile = new File(filename);
332
+            if (transferType == TransferType.RECEIVE) {
333
+                fileOut = new DataOutputStream(new FileOutputStream(transferFile.getAbsolutePath(), (startpos > 0)));
334
+            }
335
+            out = new DataOutputStream(socket.getOutputStream());
336
+            in = new DataInputStream(socket.getInputStream());
337
+            if (handler != null) {
338
+                handler.socketOpened(this);
339
+            }
340
+        } catch (IOException ioe) {
341
+            socketClosed();
342
+        }
343
+    }
344
+
345
+    /**
346
+     * Called when the socket is closed, before the thread terminates.
347
+     */
348
+    @Override
349
+    protected void socketClosed() {
350
+        // Try to close both, even if one fails.
351
+        if (out != null) {
352
+            try {
353
+                out.close();
354
+            } catch (IOException e) {
355
+            }
356
+        }
357
+        if (in != null) {
358
+            try {
359
+                in.close();
360
+            } catch (IOException e) {
361
+            }
362
+        }
363
+        out = null;
364
+        in = null;
365
+        if (handler != null) {
366
+            handler.socketClosed(this);
367
+        }
368
+        // Try to delete empty files.
369
+        if (transferType == TransferType.RECEIVE && transferFile.length() == 0) {
370
+            transferFile.delete();
371
+        }
372
+        synchronized (SENDS) {
373
+            SENDS.remove(this);
374
+        }
375
+    }
376
+
377
+    /**
378
+     * Handle the socket.
379
+     *
380
+     * @return false when socket is closed, true will cause the method to be
381
+     *         called again.
382
+     */
383
+    @Override
384
+    protected boolean handleSocket() {
385
+        if (out == null || in == null) {
386
+            return false;
387
+        }
388
+        if (transferType == TransferType.RECEIVE) {
389
+            return handleReceive();
390
+        } else {
391
+            return handleSend();
392
+        }
393
+    }
394
+
395
+    /**
396
+     * Handle the socket as a RECEIVE.
397
+     *
398
+     * @return false when socket is closed (or should be closed), true will cause the method to be
399
+     *         called again.
400
+     */
401
+    protected boolean handleReceive() {
402
+        try {
403
+            final byte[] data = new byte[blockSize];
404
+            final int bytesRead = in.read(data);
405
+            readSize = readSize + bytesRead;
406
+
407
+            if (bytesRead > 0) {
408
+                if (handler != null) {
409
+                    handler.dataTransfered(this, bytesRead);
410
+                }
411
+                fileOut.write(data, 0, bytesRead);
412
+                // Send ack
413
+                out.writeInt((int) readSize);
414
+                out.flush();
415
+                if (readSize == size) {
416
+                    fileOut.close();
417
+                    return false;
418
+                } else {
419
+                    return true;
420
+                }
421
+            } else if (bytesRead < 0) {
422
+                fileOut.close();
423
+                return false;
424
+            }
425
+        } catch (IOException e) {
426
+            return false;
427
+        }
428
+        return false;
429
+    }
430
+
431
+    /**
432
+     * Handle the socket as a SEND.
433
+     *
434
+     * @return false when socket is closed (or should be closed), true will cause the method to be
435
+     *         called again.
436
+     */
437
+    protected boolean handleSend() {
438
+        try {
439
+            final byte[] data = new byte[blockSize];
440
+            int bytesRead = fileIn.read(data);
441
+            readSize = readSize + bytesRead;
442
+
443
+            if (bytesRead > 0) {
444
+                if (handler != null) {
445
+                    handler.dataTransfered(this, bytesRead);
446
+                }
447
+                out.write(data, 0, bytesRead);
448
+                out.flush();
449
+
450
+                // Wait for acknowlegement packet.
451
+                if (!turbo) {
452
+                    int bytesRecieved;
453
+                    do {
454
+                        bytesRecieved = in.readInt();
455
+                    } while ((readSize - bytesRecieved) > 0);
456
+                }
457
+
458
+                if (readSize == size) {
459
+                    fileIn.close();
460
+
461
+                    // Process all the ack packets that may have been sent.
462
+                    // In true turbo dcc mode, none will have been sent and the socket
463
+                    // will just close, in fast-dcc mode all the acks will be here,
464
+                    // So keep reading acks untill the socket closes (IOException) or we
465
+                    // have recieved all the acks.
466
+                    if (turbo) {
467
+                        int ack = 0;
468
+                        do {
469
+                            try {
470
+                                ack = in.readInt();
471
+                            } catch (IOException e) {
472
+                                break;
473
+                            }
474
+                        } while (ack > 0 && (readSize - ack) > 0);
475
+                    }
476
+
477
+                    return false;
478
+                }
479
+
480
+                return true;
481
+            } else if (bytesRead < 0) {
482
+                fileIn.close();
483
+                return true;
484
+            }
485
+        } catch (IOException e) {
486
+            return false;
487
+        }
488
+        return false;
489
+    }
490
+
491
+}

+ 54
- 0
src/com/dmdirc/addons/dcc/DCCSendInterface.java View File

@@ -0,0 +1,54 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+/**
26
+ * This interfaces allows DCC Send Windows to receive data from a DCCSend
27
+ *
28
+ * @author Shane 'Dataforce' McCormack
29
+ */
30
+public interface DCCSendInterface {
31
+
32
+    /**
33
+     * Called when the socket is closed
34
+     *
35
+     * @param dcc The DCCSend that this message is from
36
+     */
37
+    void socketClosed(final DCCSend dcc);
38
+
39
+    /**
40
+     * Called when the socket is opened
41
+     *
42
+     * @param dcc The DCCSend that this message is from
43
+     */
44
+    void socketOpened(final DCCSend dcc);
45
+
46
+    /**
47
+     * Called when data is sent/recieved
48
+     *
49
+     * @param dcc The DCCSend that this message is from
50
+     * @param bytes The number of new bytes that were transfered
51
+     */
52
+    void dataTransfered(final DCCSend dcc, final int bytes);
53
+
54
+}

+ 339
- 0
src/com/dmdirc/addons/dcc/DCCSendWindow.java View File

@@ -0,0 +1,339 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc;
24
+
25
+import com.dmdirc.Server;
26
+import com.dmdirc.ServerState;
27
+import com.dmdirc.actions.ActionManager;
28
+import com.dmdirc.addons.dcc.actions.DCCActions;
29
+import com.dmdirc.config.IdentityManager;
30
+import com.dmdirc.parser.interfaces.Parser;
31
+import com.dmdirc.parser.interfaces.callbacks.SocketCloseListener;
32
+
33
+import java.awt.event.ActionEvent;
34
+import java.awt.event.ActionListener;
35
+import java.io.File;
36
+
37
+import javax.swing.JButton;
38
+import javax.swing.JLabel;
39
+import javax.swing.JOptionPane;
40
+import javax.swing.JProgressBar;
41
+
42
+import net.miginfocom.swing.MigLayout;
43
+
44
+/**
45
+ * This class links DCC Send objects to a window.
46
+ *
47
+ * @author Shane 'Dataforce' McCormack
48
+ */
49
+public class DCCSendWindow extends DCCFrame implements DCCSendInterface, ActionListener, SocketCloseListener {
50
+
51
+    /** The DCCSend object we are a window for */
52
+    private final DCCSend dcc;
53
+
54
+    /** Other Nickname */
55
+    private final String otherNickname;
56
+
57
+    /** Total data transfered */
58
+    private volatile long transferCount = 0;
59
+
60
+    /** Time Started */
61
+    private long timeStarted = 0;
62
+
63
+    /** Progress Bar */
64
+    private final JProgressBar progress = new JProgressBar();
65
+
66
+    /** Status Label */
67
+    private final JLabel status = new JLabel("Status: Waiting");
68
+
69
+    /** Speed Label */
70
+    private final JLabel speed = new JLabel("Speed: Unknown");
71
+
72
+    /** Time Label */
73
+    private final JLabel remaining = new JLabel("Time Remaining: Unknown");
74
+
75
+    /** Time Taken */
76
+    private final JLabel taken = new JLabel("Time Taken: 00:00");
77
+
78
+    /** Button */
79
+    private final JButton button = new JButton("Cancel");
80
+
81
+    /** Plugin that this send belongs to. */
82
+    private final DCCPlugin myPlugin;
83
+
84
+    /** IRC Parser that caused this send */
85
+    private Parser parser = null;
86
+
87
+    /** Server that caused this send */
88
+    private Server server = null;
89
+
90
+    /**
91
+     * Creates a new instance of DCCSendWindow with a given DCCSend object.
92
+     *
93
+     * @param plugin the DCC Plugin responsible for this window
94
+     * @param dcc The DCCSend object this window wraps around
95
+     * @param title The title of this window
96
+     * @param targetNick Nickname of target
97
+     * @param server The server that initiated this send
98
+     */
99
+    public DCCSendWindow(final DCCPlugin plugin, final DCCSend dcc, final String title, final String targetNick, final Server server) {
100
+        super(plugin, title, dcc.getType() == DCCSend.TransferType.SEND ? "dcc-send-inactive" : "dcc-receive-inactive");
101
+        this.dcc = dcc;
102
+        this.server = server;
103
+        this.parser = server == null ? null : server.getParser();
104
+        this.myPlugin = plugin;
105
+
106
+        if (parser != null) {
107
+            parser.getCallbackManager().addNonCriticalCallback(SocketCloseListener.class, this);
108
+        }
109
+        dcc.setHandler(this);
110
+
111
+        otherNickname = targetNick;
112
+
113
+        getContentPane().setLayout(new MigLayout());
114
+
115
+        progress.setMinimum(0);
116
+        progress.setMaximum(100);
117
+        progress.setStringPainted(true);
118
+        progress.setValue(0);
119
+
120
+        if (dcc.getType() == DCCSend.TransferType.SEND) {
121
+            getContentPane().add(new JLabel("Sending: " + dcc.getShortFileName()), "wrap");
122
+            getContentPane().add(new JLabel("To: " + targetNick), "wrap");
123
+        } else {
124
+            getContentPane().add(new JLabel("Recieving: " + dcc.getShortFileName()), "wrap");
125
+            getContentPane().add(new JLabel("From: " + targetNick), "wrap");
126
+        }
127
+        getContentPane().add(status, "wrap");
128
+        getContentPane().add(speed, "wrap");
129
+        getContentPane().add(remaining, "wrap");
130
+        getContentPane().add(taken, "wrap");
131
+        getContentPane().add(progress, "growx, wrap");
132
+
133
+        button.addActionListener(this);
134
+        getContentPane().add(button, "wrap, align right");
135
+
136
+        plugin.addWindow(this);
137
+    }
138
+
139
+    /** {@inheritDoc} */
140
+    @Override
141
+    public void onSocketClosed(final Parser tParser) {
142
+        // Remove our reference to the parser (and its reference to us)
143
+        parser.getCallbackManager().delAllCallback(this);
144
+        parser = null;
145
+        // Can't resend without the parser.
146
+        if ("Resend".equals(button.getText())) {
147
+            button.setText("Close Window");
148
+        }
149
+    }
150
+
151
+    /**
152
+     * Get the DCCSend Object associated with this window
153
+     *
154
+     * @return The DCCSend Object associated with this window
155
+     */
156
+    public DCCSend getDCC() {
157
+        return dcc;
158
+    }
159
+
160
+    /** {@inheritDoc} */
161
+    @Override
162
+    public void actionPerformed(final ActionEvent e) {
163
+        if (e.getActionCommand().equals("Cancel")) {
164
+            if (dcc.getType() == DCCSend.TransferType.SEND) {
165
+                button.setText("Resend");
166
+            } else {
167
+                button.setText("Close Window");
168
+            }
169
+            status.setText("Status: Cancelled");
170
+            dcc.close();
171
+        } else if (e.getActionCommand().equals("Resend")) {
172
+            button.setText("Cancel");
173
+            status.setText("Status: Resending...");
174
+            synchronized (this) {
175
+                transferCount = 0;
176
+            }
177
+            dcc.reset();
178
+            if (parser != null && server.getState() == ServerState.CONNECTED) {
179
+                final String myNickname = parser.getLocalClient().getNickname();
180
+                // Check again incase we have changed nickname to the same nickname that
181
+                // this send is for.
182
+                if (parser.getStringConverter().equalsIgnoreCase(otherNickname, myNickname)) {
183
+                    final Thread errorThread = new Thread(new Runnable() {
184
+
185
+                        /** {@inheritDoc} */
186
+                        @Override
187
+                        public void run() {
188
+                            JOptionPane.showMessageDialog(null, "You can't DCC yourself.", "DCC Error", JOptionPane.ERROR_MESSAGE);
189
+                        }
190
+
191
+                    });
192
+                    errorThread.start();
193
+                    return;
194
+                } else {
195
+                    if (IdentityManager.getGlobalConfig().getOptionBool(plugin.getDomain(), "send.reverse")) {
196
+                        parser.sendCTCP(otherNickname, "DCC", "SEND \"" + (new File(dcc.getFileName())).getName() + "\" " + DCC.ipToLong(myPlugin.getListenIP(parser)) + " 0 " + dcc.getFileSize() + " " + dcc.makeToken() + ((dcc.isTurbo()) ? " T" : ""));
197
+                        return;
198
+                    } else if (plugin.listen(dcc)) {
199
+                        parser.sendCTCP(otherNickname, "DCC", "SEND \"" + (new File(dcc.getFileName())).getName() + "\" " + DCC.ipToLong(myPlugin.getListenIP(parser)) + " " + dcc.getPort() + " " + dcc.getFileSize() + ((dcc.isTurbo()) ? " T" : ""));
200
+                        return;
201
+                    }
202
+                }
203
+            } else {
204
+                status.setText("Status: Resend failed.");
205
+                button.setText("Close Window");
206
+            }
207
+        } else if (e.getActionCommand().equals("Close Window")) {
208
+            close();
209
+        }
210
+    }
211
+
212
+    /**
213
+     * Called when data is sent/recieved
214
+     *
215
+     * @param dcc The DCCSend that this message is from
216
+     * @param bytes The number of new bytes that were transfered
217
+     */
218
+    @Override
219
+    public void dataTransfered(final DCCSend dcc, final int bytes) {
220
+        final double percent;
221
+        synchronized (this) {
222
+            transferCount += bytes;
223
+            percent = (100.00 / dcc.getFileSize()) * (transferCount + dcc.getFileStart());
224
+        }
225
+
226
+        if (dcc.getType() == DCCSend.TransferType.SEND) {
227
+            status.setText("Status: Sending");
228
+        } else {
229
+            status.setText("Status: Recieving");
230
+        }
231
+
232
+        updateSpeedAndTime();
233
+
234
+        progress.setValue((int) Math.floor(percent));
235
+
236
+        ActionManager.processEvent(DCCActions.DCC_SEND_DATATRANSFERED, null, this, bytes);
237
+    }
238
+
239
+    /**
240
+     * Update the transfer speed, time remaining and time taken labels.
241
+     */
242
+    public void updateSpeedAndTime() {
243
+        final long time = (System.currentTimeMillis() - timeStarted) / 1000;
244
+        final double bytesPerSecond;
245
+        synchronized (this) {
246
+            bytesPerSecond = (time > 0) ? (transferCount / time) : transferCount;
247
+        }
248
+
249
+        if (bytesPerSecond > 1048576) {
250
+            speed.setText(String.format("Speed: %.2f MB/s", (bytesPerSecond / 1048576)));
251
+        } else if (bytesPerSecond > 1024) {
252
+            speed.setText(String.format("Speed: %.2f KB/s", (bytesPerSecond / 1024)));
253
+        } else {
254
+            speed.setText(String.format("Speed: %f B/s", bytesPerSecond));
255
+        }
256
+
257
+        final long remaningBytes;
258
+        synchronized (this) {
259
+            remaningBytes = dcc.getFileSize() - dcc.getFileStart() - transferCount;
260
+        }
261
+        final double remainingSeconds = (bytesPerSecond > 0) ? (remaningBytes / bytesPerSecond) : 1;
262
+
263
+        remaining.setText(String.format("Time Remaining: %s", duration((int) Math.floor(remainingSeconds))));
264
+        taken.setText(String.format("Time Taken: %s", timeStarted == 0 ? "N/A" : duration(time)));
265
+    }
266
+
267
+    /**
268
+     * Get the duration in seconds as a string.
269
+     *
270
+     * @param secondsInput to get duration for
271
+     * @return Duration as a string
272
+     */
273
+    private String duration(final long secondsInput) {
274
+        final StringBuilder result = new StringBuilder();
275
+        final long hours = (secondsInput / 3600);
276
+        final long minutes = (secondsInput / 60 % 60);
277
+        final long seconds = (secondsInput % 60);
278
+
279
+        if (hours > 0) {
280
+            result.append(hours + ":");
281
+        }
282
+        result.append(String.format("%0,2d:%0,2d", minutes, seconds));
283
+
284
+        return result.toString();
285
+    }
286
+
287
+    /**
288
+     * Called when the socket is closed
289
+     *
290
+     * @param dcc The DCCSend that this message is from
291
+     */
292
+    @Override
293
+    public void socketClosed(final DCCSend dcc) {
294
+        ActionManager.processEvent(DCCActions.DCC_SEND_SOCKETCLOSED, null, this);
295
+        if (!isWindowClosing()) {
296
+            synchronized (this) {
297
+                if (transferCount == dcc.getFileSize()) {
298
+                    status.setText("Status: Transfer Compelete.");
299
+                    progress.setValue(100);
300
+                    setIcon(dcc.getType() == DCCSend.TransferType.SEND ? "dcc-send-done" : "dcc-receive-done");
301
+                    button.setText("Close Window");
302
+                } else {
303
+                    status.setText("Status: Transfer Failed.");
304
+                    setIcon(dcc.getType() == DCCSend.TransferType.SEND ? "dcc-send-failed" : "dcc-receive-failed");
305
+                    if (dcc.getType() == DCCSend.TransferType.SEND) {
306
+                        button.setText("Resend");
307
+                    } else {
308
+                        button.setText("Close Window");
309
+                    }
310
+                }
311
+            }
312
+            updateSpeedAndTime();
313
+        }
314
+    }
315
+
316
+    /**
317
+     * Called when the socket is opened
318
+     *
319
+     * @param dcc The DCCSend that this message is from
320
+     */
321
+    @Override
322
+    public void socketOpened(final DCCSend dcc) {
323
+        ActionManager.processEvent(DCCActions.DCC_SEND_SOCKETOPENED, null, this);
324
+        status.setText("Status: Socket Opened");
325
+        timeStarted = System.currentTimeMillis();
326
+        setIcon(dcc.getType() == DCCSend.TransferType.SEND ? "dcc-send-active" : "dcc-receive-active");
327
+    }
328
+
329
+    /**
330
+     * Closes this container (and it's associated frame).
331
+     */
332
+    @Override
333
+    public void windowClosing() {
334
+        super.windowClosing();
335
+        dcc.removeFromSends();
336
+        dcc.close();
337
+    }
338
+
339
+}

+ 86
- 0
src/com/dmdirc/addons/dcc/actions/DCCActions.java View File

@@ -0,0 +1,86 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc.actions;
24
+
25
+import com.dmdirc.actions.interfaces.ActionMetaType;
26
+import com.dmdirc.actions.interfaces.ActionType;
27
+
28
+/**
29
+ * DCC actions.
30
+ *
31
+ * @author chris
32
+ */
33
+public enum DCCActions implements ActionType {
34
+
35
+    /** DCC Chat Request. */
36
+    DCC_CHAT_REQUEST(DCCEvents.DCC_CHAT_REQUEST, "DCC Chat Requested"),
37
+    /** DCC Chat Request Sent. */
38
+    DCC_CHAT_REQUEST_SENT(DCCEvents.DCC_CHAT_REQUEST_SENT, "DCC Chat Request Sent"),
39
+    /** DCC Message from another person. */
40
+    DCC_CHAT_MESSAGE(DCCEvents.DCC_CHAT_MESSAGE, "DCC Chat Message Recieved"),
41
+    /** DCC Message to another person. */
42
+    DCC_CHAT_SELFMESSAGE(DCCEvents.DCC_CHAT_SELFMESSAGE, "DCC Chat Message Sent"),
43
+    /** DCC Chat Socket Closed. */
44
+    DCC_CHAT_SOCKETCLOSED(DCCEvents.DCC_CHAT_SOCKETCLOSED, "DCC Chat Socket Closed"),
45
+    /** DCC Chat Socket Opened. */
46
+    DCC_CHAT_SOCKETOPENED(DCCEvents.DCC_CHAT_SOCKETOPENED, "DCC Chat Socket Opened"),
47
+    /** DCC Send Socket Closed. */
48
+    DCC_SEND_SOCKETCLOSED(DCCEvents.DCC_SEND_SOCKETCLOSED, "DCC Send Socket Closed"),
49
+    /** DCC Send Socket Opened. */
50
+    DCC_SEND_SOCKETOPENED(DCCEvents.DCC_SEND_SOCKETOPENED, "DCC Send Socket Opened"),
51
+    /** DCC Send Data Transfered */
52
+    DCC_SEND_DATATRANSFERED(DCCEvents.DCC_SEND_DATATRANSFERED, "DCC Send Socket Closed"),
53
+    /** DCC Send Request. */
54
+    DCC_SEND_REQUEST(DCCEvents.DCC_SEND_REQUEST, "DCC Chat Requested"),
55
+    /** DCC Send Request Sent. */
56
+    DCC_SEND_REQUEST_SENT(DCCEvents.DCC_SEND_REQUEST_SENT, "DCC Chat Request Sent");
57
+
58
+    /** The type of this action. */
59
+    private final ActionMetaType type;
60
+
61
+    /** The name of this action. */
62
+    private final String name;
63
+
64
+    /**
65
+     * Constructs a new core action.
66
+     * @param type The type of this action
67
+     * @param name The name of this action
68
+     */
69
+    DCCActions(final ActionMetaType type, final String name) {
70
+        this.type = type;
71
+        this.name = name;
72
+    }
73
+
74
+    /** {@inheritDoc} */
75
+    @Override
76
+    public ActionMetaType getType() {
77
+        return type;
78
+    }
79
+
80
+    /** {@inheritDoc} */
81
+    @Override
82
+    public String getName() {
83
+        return name;
84
+    }
85
+
86
+}

+ 104
- 0
src/com/dmdirc/addons/dcc/actions/DCCEvents.java View File

@@ -0,0 +1,104 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc.actions;
24
+
25
+import com.dmdirc.Server;
26
+import com.dmdirc.actions.interfaces.ActionMetaType;
27
+
28
+import com.dmdirc.addons.dcc.DCCChatWindow;
29
+import com.dmdirc.addons.dcc.DCCSendWindow;
30
+
31
+import java.io.File;
32
+
33
+/**
34
+ * Defines DCC-related events.
35
+ *
36
+ * @author Chris
37
+ */
38
+public enum DCCEvents implements ActionMetaType {
39
+
40
+    /** DCC Chat Request. */
41
+    DCC_CHAT_REQUEST(new String[]{"server", "client"}, Server.class, String.class),
42
+    /** DCC Chat Request Sent. */
43
+    DCC_CHAT_REQUEST_SENT(new String[]{"server", "client"}, Server.class, String.class),
44
+    /** DCC Message from another person. */
45
+    DCC_CHAT_MESSAGE(new String[]{"DCCChatWindow", "Nickname", "Message"}, DCCChatWindow.class, String.class, String.class),
46
+    /** DCC Message to another person. */
47
+    DCC_CHAT_SELFMESSAGE(new String[]{"DCCChatWindow", "Message"}, DCCChatWindow.class, String.class),
48
+    /** DCC Chat Socket Closed. */
49
+    DCC_CHAT_SOCKETCLOSED(new String[]{"DCCChatWindow"}, DCCChatWindow.class),
50
+    /** DCC Chat Socket Opened. */
51
+    DCC_CHAT_SOCKETOPENED(new String[]{"DCCChatWindow"}, DCCChatWindow.class),
52
+    /** DCC Send Socket Closed. */
53
+    DCC_SEND_SOCKETCLOSED(new String[]{"DCCSendWindow"}, DCCSendWindow.class),
54
+    /** DCC Send Socket Opened. */
55
+    DCC_SEND_SOCKETOPENED(new String[]{"DCCSendWindow"}, DCCSendWindow.class),
56
+    /** DCC Send Data Transfered */
57
+    DCC_SEND_DATATRANSFERED(new String[]{"DCCSendWindow", "Bytes Transfered"}, DCCSendWindow.class, int.class),
58
+    /** DCC Send Request. */
59
+    DCC_SEND_REQUEST(new String[]{"server", "client", "file"}, Server.class, String.class, String.class),
60
+    /** DCC Send Request Sent. */
61
+    DCC_SEND_REQUEST_SENT(new String[]{"server", "client", "file"}, Server.class, String.class, File.class);
62
+
63
+    /** The names of the arguments for this meta type. */
64
+    private String[] argNames;
65
+
66
+    /** The classes of the arguments for this meta type. */
67
+    private Class[] argTypes;
68
+
69
+    /**
70
+     * Creates a new instance of this meta-type.
71
+     *
72
+     * @param argNames The names of the meta-type's arguments
73
+     * @param argTypes The types of the meta-type's arguments
74
+     */
75
+    DCCEvents(final String[] argNames, final Class... argTypes) {
76
+        this.argNames = argNames;
77
+        this.argTypes = argTypes;
78
+    }
79
+
80
+    /** {@inheritDoc} */
81
+    @Override
82
+    public int getArity() {
83
+        return argNames.length;
84
+    }
85
+
86
+    /** {@inheritDoc} */
87
+    @Override
88
+    public Class[] getArgTypes() {
89
+        return argTypes;
90
+    }
91
+
92
+    /** {@inheritDoc} */
93
+    @Override
94
+    public String[] getArgNames() {
95
+        return argNames;
96
+    }
97
+
98
+    /** {@inheritDoc} */
99
+    @Override
100
+    public String getGroup() {
101
+        return "DCC Events";
102
+    }
103
+
104
+}

+ 108
- 0
src/com/dmdirc/addons/dcc/kde/KDialogProcess.java View File

@@ -0,0 +1,108 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc.kde;
24
+
25
+import java.io.File;
26
+import java.io.IOException;
27
+import java.util.ArrayList;
28
+
29
+/**
30
+ * Hold a Process and stream readers for a KDialog Process
31
+ */
32
+public class KDialogProcess {
33
+
34
+    /** Is kdialog in /bin? */
35
+    private final static boolean isBin = (new File("/bin/kdialog")).exists();
36
+
37
+    /** Does KDialog exist? */
38
+    private final static boolean hasKDialog = (new File("/usr/bin/kdialog")).exists() || isBin;
39
+
40
+    /** Stream for the stdout stream for this process */
41
+    private final StreamReader stdOutputStream;
42
+
43
+    /** Stream for the stderr stream for this process */
44
+    private final StreamReader stdErrorStream;
45
+
46
+    /** The actual process for this process */
47
+    private final Process process;
48
+
49
+    /**
50
+     * Execute kdialog with the Parameters in params
51
+     *
52
+     * @param params Parameters to pass to kdialog
53
+     */
54
+    public KDialogProcess(final String[] params) throws IOException {
55
+        final String[] exec = new String[params.length + 1];
56
+        System.arraycopy(params, 0, exec, 1, params.length);
57
+        exec[0] = (isBin) ? "/bin/kdialog" : "/usr/bin/kdialog";
58
+        process = Runtime.getRuntime().exec(exec);
59
+        stdOutputStream = new StreamReader(process.getInputStream(), new ArrayList<String>());
60
+        stdErrorStream = new StreamReader(process.getErrorStream(), new ArrayList<String>());
61
+        stdOutputStream.start();
62
+        stdErrorStream.start();
63
+    }
64
+
65
+    /**
66
+     * Does this system have kdialog?
67
+     *
68
+     * @return True if kdialog was found, else false
69
+     */
70
+    public static boolean hasKDialog() {
71
+        return hasKDialog;
72
+    }
73
+
74
+    /**
75
+     * Get the process object for this KDialogProcess
76
+     *
77
+     * @return The process object for this KDialogProcess
78
+     */
79
+    public Process getProcess() {
80
+        return process;
81
+    }
82
+
83
+    /**
84
+     * Get the StreamReader for this KDialogProcess's stdout stream
85
+     *
86
+     * @return The StreamReader for this KDialogProcess's stdout stream
87
+     */
88
+    public StreamReader getStdOutStream() {
89
+        return stdOutputStream;
90
+    }
91
+
92
+    /**
93
+     * Get the StreamReader for this KDialogProcess's stderr stream
94
+     *
95
+     * @return The StreamReader for this KDialogProcess's stderr stream
96
+     */
97
+    public StreamReader getStdErrStream() {
98
+        return stdErrorStream;
99
+    }
100
+
101
+    /**
102
+     * Wait for the process to finish.
103
+     */
104
+    public void waitFor() throws InterruptedException {
105
+        process.waitFor();
106
+    }
107
+
108
+}

+ 336
- 0
src/com/dmdirc/addons/dcc/kde/KFileChooser.java View File

@@ -0,0 +1,336 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc.kde;
24
+
25
+import com.dmdirc.addons.dcc.DCCPlugin;
26
+
27
+import com.dmdirc.config.IdentityManager;
28
+
29
+import java.awt.Component;
30
+import java.awt.HeadlessException;
31
+import java.io.File;
32
+import java.util.ArrayList;
33
+import java.util.List;
34
+
35
+import javax.swing.JFileChooser;
36
+import javax.swing.filechooser.FileSystemView;
37
+
38
+/**
39
+ * JFileChooser that uses KDialog to show the actual chooser.
40
+ * This is quite hacky, and not guarenteed to behave identically to JFileChooser,
41
+ * altho it tries to be as close as possible.
42
+ * Almost a drop in replacement for JFileChooser, replace:
43
+ *    new JFileChooser();
44
+ * with:
45
+ *    KFileChooser.getFileChooser();
46
+ *
47
+ * There are obviously some differences:
48
+ * - File filters must be set using setKDEFileFilter() not using FileFilter objects.
49
+ * - FileSystemView's are ignored
50
+ * - showOpenDialog and showSaveDialog shell kdialog, so only options available
51
+ *   in kdialog work.
52
+ * - getFileChooser() will return a JFileChooser object unless the DCC plugin's
53
+ *   config option "general.useKFileChooser" is set to "true" (defaults to false)
54
+ *   and kdialog is in either /usr/bin or /bin
55
+ * - Selection mode FILES_AND_DIRECTORIES can not be used
56
+ */
57
+public class KFileChooser extends JFileChooser {
58
+
59
+    /**
60
+     * A version number for this class.
61
+     * It should be changed whenever the class structure is changed (or anything
62
+     * else that would prevent serialized objects being unserialized with the new
63
+     * class).
64
+     */
65
+    private static final long serialVersionUID = 200806141;
66
+
67
+    /** File Filter */
68
+    private String fileFilter = null;
69
+
70
+    /** The plugin that this file chooser is for. */
71
+    private final DCCPlugin plugin;
72
+
73
+    /**
74
+     * Should a KFileChooser be used rather than a JFileChooser?
75
+     *
76
+     * @param plugin The DCC Plugin that is requesting a chooser
77
+     * @return return true if getFileChooser() will return a KFileChooser not a
78
+     *         JFileChooser
79
+     */
80
+    public static boolean useKFileChooser(final DCCPlugin plugin) {
81
+        return KDialogProcess.hasKDialog() && IdentityManager.getGlobalConfig().getOptionBool(plugin.getDomain(), "general.useKFileChooser");
82
+    }
83
+
84
+    /**
85
+     * Constructs a FileChooser pointing to the user's default directory.
86
+     *
87
+     * @param plugin The DCC Plugin that is requesting a chooser
88
+     * @return The relevant FileChooser
89
+     */
90
+    public static JFileChooser getFileChooser(final DCCPlugin plugin) {
91
+        return useKFileChooser(plugin) ? new KFileChooser(plugin) : new JFileChooser();
92
+    }
93
+
94
+    /**
95
+     * Constructs a FileChooser using the given File as the path.
96
+     *
97
+     * @param plugin The DCC Plugin that is requesting a chooser
98
+     * @param currentDirectory Directory to use as the base directory
99
+     * @return The relevant FileChooser
100
+     */
101
+    public static JFileChooser getFileChooser(final DCCPlugin plugin, final File currentDirectory) {
102
+        return useKFileChooser(plugin) ? new KFileChooser(plugin, currentDirectory) : new JFileChooser(currentDirectory);
103
+    }
104
+
105
+    /**
106
+     * Constructs a FileChooser using the given current directory and FileSystemView.
107
+     *
108
+     * @param plugin The DCC Plugin that is requesting a chooser
109
+     * @param currentDirectory Directory to use as the base directory
110
+     * @param fsv The FileSystemView to use
111
+     * @return The relevant FileChooser
112
+     */
113
+    public static JFileChooser getFileChooser(final DCCPlugin plugin, final File currentDirectory, final FileSystemView fsv) {
114
+        return useKFileChooser(plugin) ? new KFileChooser(plugin, currentDirectory, fsv) : new JFileChooser(currentDirectory, fsv);
115
+    }
116
+
117
+    /**
118
+     * Constructs a FileChooser using the given FileSystemView.
119
+     *
120
+     * @param plugin The DCC Plugin that is requesting a chooser
121
+     * @param fsv The FileSystemView to use
122
+     * @return The relevant FileChooser
123
+     */
124
+    public static JFileChooser getFileChooser(final DCCPlugin plugin, final FileSystemView fsv) {
125
+        return useKFileChooser(plugin) ? new KFileChooser(plugin, fsv) : new JFileChooser(fsv);
126
+    }
127
+
128
+    /**
129
+     * Constructs a FileChooser using the given path.
130
+     *
131
+     * @param plugin The DCC Plugin that is requesting a chooser
132
+     * @param currentDirectoryPath Directory to use as the base directory
133
+     * @return The relevant FileChooser
134
+     */
135
+    public static JFileChooser getFileChooser(final DCCPlugin plugin, final String currentDirectoryPath) {
136
+        return useKFileChooser(plugin) ? new KFileChooser(plugin, currentDirectoryPath) : new JFileChooser(currentDirectoryPath);
137
+    }
138
+
139
+    /**
140
+     * Constructs a FileChooser using the given current directory path and FileSystemView.
141
+     *
142
+     * @param plugin The DCC Plugin that is requesting a chooser
143
+     * @param currentDirectoryPath Directory to use as the base directory
144
+     * @param fsv The FileSystemView to use
145
+     * @return The relevant FileChooser
146
+     */
147
+    public static JFileChooser getFileChooser(final DCCPlugin plugin, final String currentDirectoryPath, final FileSystemView fsv) {
148
+        return useKFileChooser(plugin) ? new KFileChooser(plugin, currentDirectoryPath, fsv) : new JFileChooser(currentDirectoryPath, fsv);
149
+    }
150
+
151
+    /**
152
+     * Constructs a FileChooser pointing to the user's default directory.
153
+     *
154
+     * @param plugin The plugin that owns this KFileChooser
155
+     */
156
+    private KFileChooser(final DCCPlugin plugin) {
157
+        super();
158
+
159
+        this.plugin = plugin;
160
+    }
161
+
162
+    /**
163
+     * Constructs a FileChooser using the given File as the path.
164
+     *
165
+     * @param plugin The plugin that owns this KFileChooser
166
+     * @param currentDirectory Directory to use as the base directory
167
+     */
168
+    private KFileChooser(final DCCPlugin plugin, final File currentDirectory) {
169
+        super(currentDirectory);
170
+
171
+        this.plugin = plugin;
172
+    }
173
+
174
+    /**
175
+     * Constructs a FileChooser using the given current directory and FileSystemView.
176
+     *
177
+     * @param plugin The plugin that owns this KFileChooser
178
+     * @param currentDirectory Directory to use as the base directory
179
+     * @param fsv The FileSystemView to use
180
+     */
181
+    private KFileChooser(final DCCPlugin plugin, final File currentDirectory, final FileSystemView fsv) {
182
+        super(currentDirectory, fsv);
183
+
184
+        this.plugin = plugin;
185
+    }
186
+
187
+    /**
188
+     * Constructs a FileChooser using the given FileSystemView.
189
+     *
190
+     * @param plugin The plugin that owns this KFileChooser
191
+     * @param fsv The FileSystemView to use
192
+     */
193
+    private KFileChooser(final DCCPlugin plugin, final FileSystemView fsv) {
194
+        super(fsv);
195
+
196
+        this.plugin = plugin;
197
+    }
198
+
199
+    /**
200
+     * Constructs a FileChooser using the given path.
201
+     *
202
+     * @param plugin The plugin that owns this KFileChooser
203
+     * @param currentDirectoryPath Directory to use as the base directory
204
+     */
205
+    private KFileChooser(final DCCPlugin plugin, final String currentDirectoryPath) {
206
+        super(currentDirectoryPath);
207
+
208
+        this.plugin = plugin;
209
+    }
210
+
211
+    /**
212
+     * Constructs a FileChooser using the given current directory path and FileSystemView.
213
+     *
214
+     * @param plugin The plugin that owns this KFileChooser
215
+     * @param currentDirectoryPath Directory to use as the base directory
216
+     * @param fsv The FileSystemView to use
217
+     */
218
+    private KFileChooser(final DCCPlugin plugin, final String currentDirectoryPath, final FileSystemView fsv) {
219
+        super(currentDirectoryPath, fsv);
220
+
221
+        this.plugin = plugin;
222
+    }
223
+
224
+    /**
225
+     * Set the file filter.
226
+     *
227
+     * @param fileFilter File filter (eg "*.php *.jpg" or null for no filter)
228
+     */
229
+    public void setKDEFileFilter(final String fileFilter) {
230
+        this.fileFilter = fileFilter;
231
+    }
232
+
233
+    /**
234
+     * Get the file filter.
235
+     *
236
+     * @return File filter (eg "*.php *.jpg" or null for no filter)
237
+     */
238
+    public String getKDEFileFilter() {
239
+        return fileFilter;
240
+    }
241
+
242
+    /** {@inheritDoc} */
243
+    @Override
244
+    public int showOpenDialog(final Component parent) throws HeadlessException {
245
+        if (!useKFileChooser(plugin)) {
246
+            return super.showOpenDialog(parent);
247
+        }
248
+        final ArrayList<String> params = new ArrayList<String>();
249
+        if (isMultiSelectionEnabled()) {
250
+            params.add("--multiple");
251
+            params.add("--separate-output");
252
+        }
253
+        if (getDialogTitle() != null && !getDialogTitle().isEmpty()) {
254
+            params.add("--caption");
255
+            params.add(getDialogTitle());
256
+        }
257
+        if (getFileSelectionMode() == DIRECTORIES_ONLY) {
258
+            params.add("--getexistingdirectory");
259
+        } else {
260
+            params.add("--getopenfilename");
261
+        }
262
+        if (getSelectedFile() != null && getFileSelectionMode() != DIRECTORIES_ONLY && !getSelectedFile().getPath().isEmpty()) {
263
+            if (getSelectedFile().getPath().charAt(0) != '/') {
264
+                params.add(getCurrentDirectory().getPath() + File.separator + getSelectedFile().getPath());
265
+            } else {
266
+                params.add(getSelectedFile().getPath());
267
+            }
268
+        } else if (getCurrentDirectory() != null) {
269
+            params.add(getCurrentDirectory().getPath());
270
+        }
271
+        if (getFileSelectionMode() != DIRECTORIES_ONLY && fileFilter != null && !fileFilter.isEmpty()) {
272
+            params.add(fileFilter);
273
+        }
274
+        KDialogProcess kdp;
275
+        try {
276
+            kdp = new KDialogProcess(params.toArray(new String[0]));
277
+            kdp.waitFor();
278
+        } catch (Exception e) {
279
+            return JFileChooser.ERROR_OPTION;
280
+        }
281
+
282
+        if (kdp.getProcess().exitValue() == 0) {
283
+            if (isMultiSelectionEnabled()) {
284
+                final List<String> list = kdp.getStdOutStream().getList();
285
+                final File[] fileList = new File[list.size()];
286
+                for (int i = 0; i < list.size(); ++i) {
287
+                    fileList[i] = new File(list.get(i));
288
+                }
289
+                setSelectedFiles(fileList);
290
+            } else {
291
+                setSelectedFile(new File(kdp.getStdOutStream().getList().get(0)));
292
+            }
293
+            return JFileChooser.APPROVE_OPTION;
294
+        } else {
295
+            return JFileChooser.ERROR_OPTION;
296
+        }
297
+    }
298
+
299
+    /** {@inheritDoc} */
300
+    @Override
301
+    public int showSaveDialog(final Component parent) throws HeadlessException {
302
+        if (!useKFileChooser(plugin)) {
303
+            return super.showSaveDialog(parent);
304
+        }
305
+        final ArrayList<String> params = new ArrayList<String>();
306
+        if (getDialogTitle() != null && !getDialogTitle().isEmpty()) {
307
+            params.add("--caption");
308
+            params.add(getDialogTitle());
309
+        }
310
+        params.add("--getsavefilename");
311
+        if (getSelectedFile() != null && !getSelectedFile().getPath().isEmpty()) {
312
+            if (getSelectedFile().getPath().charAt(0) != '/') {
313
+                params.add(getCurrentDirectory().getPath() + File.separator + getSelectedFile().getPath());
314
+            } else {
315
+                params.add(getSelectedFile().getPath());
316
+            }
317
+        } else if (getCurrentDirectory() != null) {
318
+            params.add(getCurrentDirectory().getPath());
319
+        }
320
+        KDialogProcess kdp;
321
+        try {
322
+            kdp = new KDialogProcess(params.toArray(new String[0]));
323
+            kdp.waitFor();
324
+        } catch (Exception e) {
325
+            return JFileChooser.ERROR_OPTION;
326
+        }
327
+
328
+        if (kdp.getProcess().exitValue() == 0) {
329
+            setSelectedFile(new File(kdp.getStdOutStream().getList().get(0)));
330
+            return JFileChooser.APPROVE_OPTION;
331
+        } else {
332
+            return JFileChooser.ERROR_OPTION;
333
+        }
334
+    }
335
+
336
+}

+ 101
- 0
src/com/dmdirc/addons/dcc/kde/StreamReader.java View File

@@ -0,0 +1,101 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcc.kde;
24
+
25
+import java.io.BufferedReader;
26
+import java.io.IOException;
27
+import java.io.InputStream;
28
+import java.io.InputStreamReader;
29
+import java.util.List;
30
+
31
+public class StreamReader extends Thread {
32
+
33
+    /** This is the Input Stream we are reading */
34
+    private InputStream stream;
35
+
36
+    /** This is the output Prefix */
37
+    private String prefix = null;
38
+
39
+    /** List to store output in */
40
+    private List<String> list = null;
41
+
42
+    /**
43
+     * Create a new Stream Reader
44
+     *
45
+     * @param stream The stream to read
46
+     * @param list The list to store the output from the stream in (null for no saving)
47
+     */
48
+    public StreamReader(final InputStream stream, final List<String> list) {
49
+        this.stream = stream;
50
+        this.list = list;
51
+    }
52
+
53
+    /**
54
+     * Create a new Stream Reader that outputs what it reads
55
+     *
56
+     * @param stream The stream to read
57
+     * @param list The list to store the output from the stream in (null for no saving)
58
+     * @param prefix Prefix of outputed messages
59
+     */
60
+    public StreamReader(final InputStream stream, final List<String> list, final String prefix) {
61
+        this.stream = stream;
62
+        this.prefix = prefix;
63
+        this.list = list;
64
+
65
+        System.out.printf("[%s] Started%n", prefix);
66
+    }
67
+
68
+    /**
69
+     * Get the list that the output is being stored in.
70
+     */
71
+    public List<String> getList() {
72
+        return list;
73
+    }
74
+
75
+    /**
76
+     * Wait for input on stream, and output/throw away/save to list
77
+     */
78
+    public void run() {
79
+        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
80
+        try {
81
+            String line;
82
+            while ((line = reader.readLine()) != null) {
83
+                if (prefix != null) {
84
+                    System.out.printf("[%s] %s%n", prefix, line);
85
+                }
86
+                if (list != null) {
87
+                    list.add(line);
88
+                }
89
+            }
90
+        } catch (IOException e) {
91
+            e.printStackTrace();
92
+        } finally {
93
+            try {
94
+                stream.close();
95
+            } catch (IOException e) {
96
+                e.printStackTrace();
97
+            }
98
+        }
99
+    }
100
+
101
+}

+ 74
- 0
src/com/dmdirc/addons/dcc/plugin.config View File

@@ -0,0 +1,74 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  requires
9
+  updates
10
+  defaults
11
+  formatters
12
+  icons
13
+  version
14
+
15
+metadata:
16
+  author=Shane <shane@dmdirc.com>
17
+  mainclass=com.dmdirc.addons.dcc.DCCPlugin
18
+  description=Adds DCC Support to dmdirc
19
+  name=dcc
20
+  nicename=DCC Plugin
21
+
22
+requires:
23
+  parent=ui_swing
24
+
25
+updates:
26
+  id=23
27
+
28
+version:
29
+  friendly=0.4
30
+
31
+persistent:
32
+  com.dmdirc.addons.dcc.actions.DCCActions
33
+  com.dmdirc.addons.dcc.actions.DCCEvents
34
+
35
+provides:
36
+  dcc command
37
+  dcc feature
38
+
39
+required-services:
40
+  swing ui
41
+
42
+defaults:
43
+  general.useKFileChooser=false
44
+  send.reverse=false
45
+  send.forceturbo=true
46
+  receive.reverse.sendtoken=false
47
+  send.blocksize=1024
48
+  receive.autoaccept=false
49
+  firewall.ip=
50
+  firewall.ports.usePortRange=false
51
+  firewall.ports.startPort=11000
52
+  firewall.ports.endPort=11019
53
+
54
+formatters:
55
+  DCCChatStarting=Starting DCC Chat with: %1$s on %2$s:%3$s
56
+  DCCChatInfo=%1$s
57
+  DCCChatError=\u00034 Error: %1$s
58
+  DCCSendError=\u00034 Error: %1$s
59
+  DCCChatSelfMessage=<%1$s> %2$s
60
+  DCCChatMessage=<%1$s> %2$s
61
+
62
+icons:
63
+  category-dcc=plugin://dcc:com/dmdirc/addons/dcc/res/transfers.png
64
+  dcc=plugin://dcc:com/dmdirc/addons/dcc/res/transfers.png
65
+  dcc-chat-active=plugin://dcc:com/dmdirc/addons/dcc/res/chat.png
66
+  dcc-chat-inactive=plugin://dcc:com/dmdirc/addons/dcc/res/chat-inactive.png
67
+  dcc-send-active=plugin://dcc:com/dmdirc/addons/dcc/res/send.png
68
+  dcc-send-inactive=plugin://dcc:com/dmdirc/addons/dcc/res/send-inactive.png
69
+  dcc-send-done=plugin://dcc:com/dmdirc/addons/dcc/res/send-done.png
70
+  dcc-send-failed=plugin://dcc:com/dmdirc/addons/dcc/res/send-failed.png
71
+  dcc-receive-active=plugin://dcc:com/dmdirc/addons/dcc/res/receive.png
72
+  dcc-receive-inactive=plugin://dcc:com/dmdirc/addons/dcc/res/receive-inactive.png
73
+  dcc-receive-done=plugin://dcc:com/dmdirc/addons/dcc/res/receive-done.png
74
+  dcc-receive-failed=plugin://dcc:com/dmdirc/addons/dcc/res/receive-failed.png

BIN
src/com/dmdirc/addons/dcc/res/chat-inactive.png View File


BIN
src/com/dmdirc/addons/dcc/res/chat.png View File


BIN
src/com/dmdirc/addons/dcc/res/receive-done.png View File


BIN
src/com/dmdirc/addons/dcc/res/receive-failed.png View File


BIN
src/com/dmdirc/addons/dcc/res/receive-inactive.png View File


BIN
src/com/dmdirc/addons/dcc/res/receive.png View File


BIN
src/com/dmdirc/addons/dcc/res/send-done.png View File


BIN
src/com/dmdirc/addons/dcc/res/send-failed.png View File


BIN
src/com/dmdirc/addons/dcc/res/send-inactive.png View File


BIN
src/com/dmdirc/addons/dcc/res/send.png View File


+ 94
- 0
src/com/dmdirc/addons/dcc/res/source/chat-inactive.svg View File

@@ -0,0 +1,94 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://web.resource.org/cc/"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.45.1"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="chat-inactive.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/transfers.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874" />
24
+  <sodipodi:namedview
25
+     id="base"
26
+     pagecolor="#ffffff"
27
+     bordercolor="#666666"
28
+     borderopacity="1.0"
29
+     inkscape:pageopacity="0.0"
30
+     inkscape:pageshadow="2"
31
+     inkscape:zoom="22.197802"
32
+     inkscape:cx="10.049476"
33
+     inkscape:cy="8.1008816"
34
+     inkscape:current-layer="layer1"
35
+     showgrid="true"
36
+     inkscape:grid-bbox="true"
37
+     inkscape:document-units="px"
38
+     inkscape:window-width="849"
39
+     inkscape:window-height="581"
40
+     inkscape:window-x="195"
41
+     inkscape:window-y="150" />
42
+  <metadata
43
+     id="metadata1877">
44
+    <rdf:RDF>
45
+      <cc:Work
46
+         rdf:about="">
47
+        <dc:format>image/svg+xml</dc:format>
48
+        <dc:type
49
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
50
+      </cc:Work>
51
+    </rdf:RDF>
52
+  </metadata>
53
+  <g
54
+     id="layer1"
55
+     inkscape:label="Layer 1"
56
+     inkscape:groupmode="layer">
57
+    <rect
58
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#b3b3b3"
59
+       id="rect4588"
60
+       width="11.064357"
61
+       height="9.19802"
62
+       x="2.5856435"
63
+       y="4.5044556" />
64
+    <path
65
+       style="fill:#ffffff;fill-rule:evenodd;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
66
+       d="M 11.361387,8.723268 L 11.885149,11.75 L 9.7678225,10.398515"
67
+       id="path3235"
68
+       sodipodi:nodetypes="ccc" />
69
+    <rect
70
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
71
+       id="rect3671"
72
+       width="11.033788"
73
+       height="2.4977343"
74
+       x="2.4819307"
75
+       y="2.5022655" />
76
+    <rect
77
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
78
+       id="rect1880"
79
+       width="11.011163"
80
+       height="10.988637"
81
+       x="2.5394683"
82
+       y="2.5619929" />
83
+    <path
84
+       sodipodi:type="arc"
85
+       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.62419522;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
86
+       id="path2262"
87
+       sodipodi:cx="7.9962873"
88
+       sodipodi:cy="8.8596535"
89
+       sodipodi:rx="4.0319309"
90
+       sodipodi:ry="2.9507425"
91
+       d="M 12.028218 8.8596535 A 4.0319309 2.9507425 0 1 1  3.9643564,8.8596535 A 4.0319309 2.9507425 0 1 1  12.028218 8.8596535 z"
92
+       transform="matrix(0.8414881,0,0,0.7625199,1.0878033,1.7443377)" />
93
+  </g>
94
+</svg>

+ 94
- 0
src/com/dmdirc/addons/dcc/res/source/chat.svg View File

@@ -0,0 +1,94 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://web.resource.org/cc/"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.45.1"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="chat.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/transfers.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874" />
24
+  <sodipodi:namedview
25
+     id="base"
26
+     pagecolor="#ffffff"
27
+     bordercolor="#666666"
28
+     borderopacity="1.0"
29
+     inkscape:pageopacity="0.0"
30
+     inkscape:pageshadow="2"
31
+     inkscape:zoom="22.197802"
32
+     inkscape:cx="10.049476"
33
+     inkscape:cy="8.1008816"
34
+     inkscape:current-layer="layer1"
35
+     showgrid="true"
36
+     inkscape:grid-bbox="true"
37
+     inkscape:document-units="px"
38
+     inkscape:window-width="849"
39
+     inkscape:window-height="581"
40
+     inkscape:window-x="195"
41
+     inkscape:window-y="150" />
42
+  <metadata
43
+     id="metadata1877">
44
+    <rdf:RDF>
45
+      <cc:Work
46
+         rdf:about="">
47
+        <dc:format>image/svg+xml</dc:format>
48
+        <dc:type
49
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
50
+      </cc:Work>
51
+    </rdf:RDF>
52
+  </metadata>
53
+  <g
54
+     id="layer1"
55
+     inkscape:label="Layer 1"
56
+     inkscape:groupmode="layer">
57
+    <rect
58
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:white"
59
+       id="rect4588"
60
+       width="11.064357"
61
+       height="9.19802"
62
+       x="2.5856435"
63
+       y="4.5044556" />
64
+    <path
65
+       style="fill:#b3b3b3;fill-rule:evenodd;stroke:#000000;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
66
+       d="M 11.361387,8.723268 L 11.885149,11.75 L 9.7678225,10.398515"
67
+       id="path3235"
68
+       sodipodi:nodetypes="ccc" />
69
+    <rect
70
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
71
+       id="rect3671"
72
+       width="11.033788"
73
+       height="2.4977343"
74
+       x="2.4819307"
75
+       y="2.5022655" />
76
+    <rect
77
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
78
+       id="rect1880"
79
+       width="11.011163"
80
+       height="10.988637"
81
+       x="2.5394683"
82
+       y="2.5619929" />
83
+    <path
84
+       sodipodi:type="arc"
85
+       style="fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.62419522;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
86
+       id="path2262"
87
+       sodipodi:cx="7.9962873"
88
+       sodipodi:cy="8.8596535"
89
+       sodipodi:rx="4.0319309"
90
+       sodipodi:ry="2.9507425"
91
+       d="M 12.028218 8.8596535 A 4.0319309 2.9507425 0 1 1  3.9643564,8.8596535 A 4.0319309 2.9507425 0 1 1  12.028218 8.8596535 z"
92
+       transform="matrix(0.8414881,0,0,0.7625199,1.0878033,1.7443377)" />
93
+  </g>
94
+</svg>

+ 96
- 0
src/com/dmdirc/addons/dcc/res/source/receive-done.svg View File

@@ -0,0 +1,96 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://creativecommons.org/ns#"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.46"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="receive-done.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/receive-done.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874">
24
+    <inkscape:perspective
25
+       sodipodi:type="inkscape:persp3d"
26
+       inkscape:vp_x="0 : 8 : 1"
27
+       inkscape:vp_y="0 : 1000 : 0"
28
+       inkscape:vp_z="16 : 8 : 1"
29
+       inkscape:persp3d-origin="8 : 5.3333333 : 1"
30
+       id="perspective2601" />
31
+  </defs>
32
+  <sodipodi:namedview
33
+     id="base"
34
+     pagecolor="#ffffff"
35
+     bordercolor="#666666"
36
+     borderopacity="1.0"
37
+     inkscape:pageopacity="0.0"
38
+     inkscape:pageshadow="2"
39
+     inkscape:zoom="31.392433"
40
+     inkscape:cx="7.4318491"
41
+     inkscape:cy="8.4618511"
42
+     inkscape:current-layer="layer1"
43
+     showgrid="true"
44
+     inkscape:grid-bbox="true"
45
+     inkscape:document-units="px"
46
+     inkscape:window-width="1062"
47
+     inkscape:window-height="667"
48
+     inkscape:window-x="195"
49
+     inkscape:window-y="150">
50
+    <inkscape:grid
51
+       type="xygrid"
52
+       id="grid2725" />
53
+  </sodipodi:namedview>
54
+  <metadata
55
+     id="metadata1877">
56
+    <rdf:RDF>
57
+      <cc:Work
58
+         rdf:about="">
59
+        <dc:format>image/svg+xml</dc:format>
60
+        <dc:type
61
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
62
+      </cc:Work>
63
+    </rdf:RDF>
64
+  </metadata>
65
+  <g
66
+     id="layer1"
67
+     inkscape:label="Layer 1"
68
+     inkscape:groupmode="layer">
69
+    <rect
70
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#ffffff"
71
+       id="rect4588"
72
+       width="11.064357"
73
+       height="9.19802"
74
+       x="2.5856435"
75
+       y="4.5044556" />
76
+    <rect
77
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
78
+       id="rect3671"
79
+       width="11.033788"
80
+       height="2.4977343"
81
+       x="2.4819307"
82
+       y="2.5022655" />
83
+    <rect
84
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
85
+       id="rect1880"
86
+       width="11.011163"
87
+       height="10.988637"
88
+       x="2.5394683"
89
+       y="2.5619929" />
90
+    <path
91
+       style="fill:none;fill-rule:evenodd;stroke:#008000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
92
+       d="M 4.6508023,9.4697644 L 6.5939458,11.476617 L 11.435877,6.5072663"
93
+       id="path2727"
94
+       sodipodi:nodetypes="ccc" />
95
+  </g>
96
+</svg>

+ 99
- 0
src/com/dmdirc/addons/dcc/res/source/receive-failed.svg View File

@@ -0,0 +1,99 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://creativecommons.org/ns#"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.46"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="receive-failed.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/send-failed.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874">
24
+    <inkscape:perspective
25
+       sodipodi:type="inkscape:persp3d"
26
+       inkscape:vp_x="0 : 8 : 1"
27
+       inkscape:vp_y="0 : 1000 : 0"
28
+       inkscape:vp_z="16 : 8 : 1"
29
+       inkscape:persp3d-origin="8 : 5.3333333 : 1"
30
+       id="perspective2601" />
31
+  </defs>
32
+  <sodipodi:namedview
33
+     id="base"
34
+     pagecolor="#ffffff"
35
+     bordercolor="#666666"
36
+     borderopacity="1.0"
37
+     inkscape:pageopacity="0.0"
38
+     inkscape:pageshadow="2"
39
+     inkscape:zoom="31.392433"
40
+     inkscape:cx="7.4318491"
41
+     inkscape:cy="8.4618511"
42
+     inkscape:current-layer="layer1"
43
+     showgrid="true"
44
+     inkscape:grid-bbox="true"
45
+     inkscape:document-units="px"
46
+     inkscape:window-width="1062"
47
+     inkscape:window-height="667"
48
+     inkscape:window-x="195"
49
+     inkscape:window-y="150">
50
+    <inkscape:grid
51
+       type="xygrid"
52
+       id="grid2725" />
53
+  </sodipodi:namedview>
54
+  <metadata
55
+     id="metadata1877">
56
+    <rdf:RDF>
57
+      <cc:Work
58
+         rdf:about="">
59
+        <dc:format>image/svg+xml</dc:format>
60
+        <dc:type
61
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
62
+      </cc:Work>
63
+    </rdf:RDF>
64
+  </metadata>
65
+  <g
66
+     id="layer1"
67
+     inkscape:label="Layer 1"
68
+     inkscape:groupmode="layer">
69
+    <rect
70
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#ffffff"
71
+       id="rect4588"
72
+       width="11.064357"
73
+       height="9.19802"
74
+       x="2.5856435"
75
+       y="4.5044556" />
76
+    <rect
77
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
78
+       id="rect3671"
79
+       width="11.033788"
80
+       height="2.4977343"
81
+       x="2.4819307"
82
+       y="2.5022655" />
83
+    <rect
84
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
85
+       id="rect1880"
86
+       width="11.011163"
87
+       height="10.988637"
88
+       x="2.5394683"
89
+       y="2.5619929" />
90
+    <path
91
+       style="fill:none;fill-rule:evenodd;stroke:#008000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
92
+       d="M 5.383463,11.699601 L 10.543942,6.4435568"
93
+       id="path2780" />
94
+    <path
95
+       style="fill:none;fill-rule:evenodd;stroke:#008000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
96
+       d="M 5.3197534,6.5072664 L 10.384668,11.604036"
97
+       id="path2782" />
98
+  </g>
99
+</svg>

+ 103
- 0
src/com/dmdirc/addons/dcc/res/source/receive-inactive.svg View File

@@ -0,0 +1,103 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://web.resource.org/cc/"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.45.1"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="receive-inactive.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/send.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874" />
24
+  <sodipodi:namedview
25
+     id="base"
26
+     pagecolor="#ffffff"
27
+     bordercolor="#666666"
28
+     borderopacity="1.0"
29
+     inkscape:pageopacity="0.0"
30
+     inkscape:pageshadow="2"
31
+     inkscape:zoom="22.197802"
32
+     inkscape:cx="-28.047481"
33
+     inkscape:cy="106.93464"
34
+     inkscape:current-layer="layer1"
35
+     showgrid="true"
36
+     inkscape:grid-bbox="true"
37
+     inkscape:document-units="px"
38
+     inkscape:window-width="849"
39
+     inkscape:window-height="581"
40
+     inkscape:window-x="195"
41
+     inkscape:window-y="150" />
42
+  <metadata
43
+     id="metadata1877">
44
+    <rdf:RDF>
45
+      <cc:Work
46
+         rdf:about="">
47
+        <dc:format>image/svg+xml</dc:format>
48
+        <dc:type
49
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
50
+      </cc:Work>
51
+    </rdf:RDF>
52
+  </metadata>
53
+  <g
54
+     id="layer1"
55
+     inkscape:label="Layer 1"
56
+     inkscape:groupmode="layer">
57
+    <rect
58
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#cccccc"
59
+       id="rect4588"
60
+       width="11.064357"
61
+       height="9.19802"
62
+       x="2.5856435"
63
+       y="4.5044556" />
64
+    <rect
65
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
66
+       id="rect3671"
67
+       width="11.033788"
68
+       height="2.4977343"
69
+       x="2.4819307"
70
+       y="2.5022655" />
71
+    <rect
72
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
73
+       id="rect1880"
74
+       width="11.011163"
75
+       height="10.988637"
76
+       x="2.5394683"
77
+       y="2.5619929" />
78
+    <rect
79
+       style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:#cccccc;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
80
+       id="rect2226"
81
+       width="7.2534657"
82
+       height="2.9999995"
83
+       x="-12.5"
84
+       y="7.5000005"
85
+       transform="scale(-1,1)" />
86
+    <path
87
+       sodipodi:type="star"
88
+       style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
89
+       id="path2228"
90
+       sodipodi:sides="3"
91
+       sodipodi:cx="9.3702974"
92
+       sodipodi:cy="7.0801978"
93
+       sodipodi:r1="4.1024742"
94
+       sodipodi:r2="2.0512371"
95
+       sodipodi:arg1="0.54230274"
96
+       sodipodi:arg2="1.5895003"
97
+       inkscape:flatsided="true"
98
+       inkscape:rounded="0"
99
+       inkscape:randomized="0"
100
+       d="M 12.884159,9.1975246 L 5.779708,9.0646277 L 9.4470255,2.9784412 L 12.884159,9.1975246 z "
101
+       transform="matrix(0,0.8351333,0.6431815,0,2.084322,1.2400099)" />
102
+  </g>
103
+</svg>

+ 111
- 0
src/com/dmdirc/addons/dcc/res/source/receive.svg View File

@@ -0,0 +1,111 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://creativecommons.org/ns#"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.46"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="receive.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/send.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874">
24
+    <inkscape:perspective
25
+       sodipodi:type="inkscape:persp3d"
26
+       inkscape:vp_x="0 : 8 : 1"
27
+       inkscape:vp_y="0 : 1000 : 0"
28
+       inkscape:vp_z="16 : 8 : 1"
29
+       inkscape:persp3d-origin="8 : 5.3333333 : 1"
30
+       id="perspective2601" />
31
+  </defs>
32
+  <sodipodi:namedview
33
+     id="base"
34
+     pagecolor="#ffffff"
35
+     bordercolor="#666666"
36
+     borderopacity="1.0"
37
+     inkscape:pageopacity="0.0"
38
+     inkscape:pageshadow="2"
39
+     inkscape:zoom="22.197802"
40
+     inkscape:cx="10.049476"
41
+     inkscape:cy="6.1075996"
42
+     inkscape:current-layer="layer1"
43
+     showgrid="true"
44
+     inkscape:grid-bbox="true"
45
+     inkscape:document-units="px"
46
+     inkscape:window-width="849"
47
+     inkscape:window-height="667"
48
+     inkscape:window-x="195"
49
+     inkscape:window-y="150" />
50
+  <metadata
51
+     id="metadata1877">
52
+    <rdf:RDF>
53
+      <cc:Work
54
+         rdf:about="">
55
+        <dc:format>image/svg+xml</dc:format>
56
+        <dc:type
57
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
58
+      </cc:Work>
59
+    </rdf:RDF>
60
+  </metadata>
61
+  <g
62
+     id="layer1"
63
+     inkscape:label="Layer 1"
64
+     inkscape:groupmode="layer">
65
+    <rect
66
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#ffffff"
67
+       id="rect4588"
68
+       width="11.064357"
69
+       height="9.19802"
70
+       x="2.5856435"
71
+       y="4.5044556" />
72
+    <rect
73
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
74
+       id="rect3671"
75
+       width="11.033788"
76
+       height="2.4977343"
77
+       x="2.4819307"
78
+       y="2.5022655" />
79
+    <rect
80
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
81
+       id="rect1880"
82
+       width="11.011163"
83
+       height="10.988637"
84
+       x="2.5394683"
85
+       y="2.5619929" />
86
+    <rect
87
+       style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
88
+       id="rect2226"
89
+       width="7.2534657"
90
+       height="2.9999995"
91
+       x="-12.5"
92
+       y="7.5000005"
93
+       transform="scale(-1,1)" />
94
+    <path
95
+       sodipodi:type="star"
96
+       style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
97
+       id="path2228"
98
+       sodipodi:sides="3"
99
+       sodipodi:cx="9.3702974"
100
+       sodipodi:cy="7.0801978"
101
+       sodipodi:r1="4.1024742"
102
+       sodipodi:r2="2.0512371"
103
+       sodipodi:arg1="0.54230274"
104
+       sodipodi:arg2="1.5895003"
105
+       inkscape:flatsided="true"
106
+       inkscape:rounded="0"
107
+       inkscape:randomized="0"
108
+       d="M 12.884159,9.1975246 L 5.779708,9.0646277 L 9.4470255,2.9784412 L 12.884159,9.1975246 z "
109
+       transform="matrix(0,0.8351333,0.6431815,0,2.084322,1.2400099)" />
110
+  </g>
111
+</svg>

+ 96
- 0
src/com/dmdirc/addons/dcc/res/source/send-done.svg View File

@@ -0,0 +1,96 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://creativecommons.org/ns#"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.46"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="send-done.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/receive-done.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874">
24
+    <inkscape:perspective
25
+       sodipodi:type="inkscape:persp3d"
26
+       inkscape:vp_x="0 : 8 : 1"
27
+       inkscape:vp_y="0 : 1000 : 0"
28
+       inkscape:vp_z="16 : 8 : 1"
29
+       inkscape:persp3d-origin="8 : 5.3333333 : 1"
30
+       id="perspective2601" />
31
+  </defs>
32
+  <sodipodi:namedview
33
+     id="base"
34
+     pagecolor="#ffffff"
35
+     bordercolor="#666666"
36
+     borderopacity="1.0"
37
+     inkscape:pageopacity="0.0"
38
+     inkscape:pageshadow="2"
39
+     inkscape:zoom="31.392433"
40
+     inkscape:cx="7.4318491"
41
+     inkscape:cy="8.4618511"
42
+     inkscape:current-layer="layer1"
43
+     showgrid="true"
44
+     inkscape:grid-bbox="true"
45
+     inkscape:document-units="px"
46
+     inkscape:window-width="1062"
47
+     inkscape:window-height="667"
48
+     inkscape:window-x="195"
49
+     inkscape:window-y="150">
50
+    <inkscape:grid
51
+       type="xygrid"
52
+       id="grid2725" />
53
+  </sodipodi:namedview>
54
+  <metadata
55
+     id="metadata1877">
56
+    <rdf:RDF>
57
+      <cc:Work
58
+         rdf:about="">
59
+        <dc:format>image/svg+xml</dc:format>
60
+        <dc:type
61
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
62
+      </cc:Work>
63
+    </rdf:RDF>
64
+  </metadata>
65
+  <g
66
+     id="layer1"
67
+     inkscape:label="Layer 1"
68
+     inkscape:groupmode="layer">
69
+    <rect
70
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#ffffff"
71
+       id="rect4588"
72
+       width="11.064357"
73
+       height="9.19802"
74
+       x="2.5856435"
75
+       y="4.5044556" />
76
+    <rect
77
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
78
+       id="rect3671"
79
+       width="11.033788"
80
+       height="2.4977343"
81
+       x="2.4819307"
82
+       y="2.5022655" />
83
+    <rect
84
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
85
+       id="rect1880"
86
+       width="11.011163"
87
+       height="10.988637"
88
+       x="2.5394683"
89
+       y="2.5619929" />
90
+    <path
91
+       style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
92
+       d="M 4.6508023,9.4697644 L 6.5939458,11.476617 L 11.435877,6.5072663"
93
+       id="path2727"
94
+       sodipodi:nodetypes="ccc" />
95
+  </g>
96
+</svg>

+ 99
- 0
src/com/dmdirc/addons/dcc/res/source/send-failed.svg View File

@@ -0,0 +1,99 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://creativecommons.org/ns#"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.46"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="send-failed.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/send-done.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874">
24
+    <inkscape:perspective
25
+       sodipodi:type="inkscape:persp3d"
26
+       inkscape:vp_x="0 : 8 : 1"
27
+       inkscape:vp_y="0 : 1000 : 0"
28
+       inkscape:vp_z="16 : 8 : 1"
29
+       inkscape:persp3d-origin="8 : 5.3333333 : 1"
30
+       id="perspective2601" />
31
+  </defs>
32
+  <sodipodi:namedview
33
+     id="base"
34
+     pagecolor="#ffffff"
35
+     bordercolor="#666666"
36
+     borderopacity="1.0"
37
+     inkscape:pageopacity="0.0"
38
+     inkscape:pageshadow="2"
39
+     inkscape:zoom="31.392433"
40
+     inkscape:cx="7.4318491"
41
+     inkscape:cy="8.4618511"
42
+     inkscape:current-layer="layer1"
43
+     showgrid="true"
44
+     inkscape:grid-bbox="true"
45
+     inkscape:document-units="px"
46
+     inkscape:window-width="1062"
47
+     inkscape:window-height="667"
48
+     inkscape:window-x="195"
49
+     inkscape:window-y="150">
50
+    <inkscape:grid
51
+       type="xygrid"
52
+       id="grid2725" />
53
+  </sodipodi:namedview>
54
+  <metadata
55
+     id="metadata1877">
56
+    <rdf:RDF>
57
+      <cc:Work
58
+         rdf:about="">
59
+        <dc:format>image/svg+xml</dc:format>
60
+        <dc:type
61
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
62
+      </cc:Work>
63
+    </rdf:RDF>
64
+  </metadata>
65
+  <g
66
+     id="layer1"
67
+     inkscape:label="Layer 1"
68
+     inkscape:groupmode="layer">
69
+    <rect
70
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#ffffff"
71
+       id="rect4588"
72
+       width="11.064357"
73
+       height="9.19802"
74
+       x="2.5856435"
75
+       y="4.5044556" />
76
+    <rect
77
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
78
+       id="rect3671"
79
+       width="11.033788"
80
+       height="2.4977343"
81
+       x="2.4819307"
82
+       y="2.5022655" />
83
+    <rect
84
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
85
+       id="rect1880"
86
+       width="11.011163"
87
+       height="10.988637"
88
+       x="2.5394683"
89
+       y="2.5619929" />
90
+    <path
91
+       style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
92
+       d="M 5.383463,11.699601 L 10.543942,6.4435568"
93
+       id="path2780" />
94
+    <path
95
+       style="fill:none;fill-rule:evenodd;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
96
+       d="M 5.3197534,6.5072664 L 10.384668,11.604036"
97
+       id="path2782" />
98
+  </g>
99
+</svg>

+ 102
- 0
src/com/dmdirc/addons/dcc/res/source/send-inactive.svg View File

@@ -0,0 +1,102 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://web.resource.org/cc/"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.45.1"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="send-inactive.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/trunk/src/com/dmdirc/res/server-normal.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874" />
24
+  <sodipodi:namedview
25
+     id="base"
26
+     pagecolor="#ffffff"
27
+     bordercolor="#666666"
28
+     borderopacity="1.0"
29
+     inkscape:pageopacity="0.0"
30
+     inkscape:pageshadow="2"
31
+     inkscape:zoom="22.197802"
32
+     inkscape:cx="10.049476"
33
+     inkscape:cy="9.9028618"
34
+     inkscape:current-layer="layer1"
35
+     showgrid="true"
36
+     inkscape:grid-bbox="true"
37
+     inkscape:document-units="px"
38
+     inkscape:window-width="849"
39
+     inkscape:window-height="581"
40
+     inkscape:window-x="195"
41
+     inkscape:window-y="150" />
42
+  <metadata
43
+     id="metadata1877">
44
+    <rdf:RDF>
45
+      <cc:Work
46
+         rdf:about="">
47
+        <dc:format>image/svg+xml</dc:format>
48
+        <dc:type
49
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
50
+      </cc:Work>
51
+    </rdf:RDF>
52
+  </metadata>
53
+  <g
54
+     id="layer1"
55
+     inkscape:label="Layer 1"
56
+     inkscape:groupmode="layer">
57
+    <rect
58
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:#b3b3b3"
59
+       id="rect4588"
60
+       width="11.064357"
61
+       height="9.19802"
62
+       x="2.5856435"
63
+       y="4.5044556" />
64
+    <rect
65
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
66
+       id="rect3671"
67
+       width="11.033788"
68
+       height="2.4977343"
69
+       x="2.4819307"
70
+       y="2.5022655" />
71
+    <rect
72
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
73
+       id="rect1880"
74
+       width="11.011163"
75
+       height="10.988637"
76
+       x="2.5394683"
77
+       y="2.5619929" />
78
+    <rect
79
+       style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#b3b3b3;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
80
+       id="rect2226"
81
+       width="7.2534657"
82
+       height="2.9999995"
83
+       x="3.5"
84
+       y="7.5000005" />
85
+    <path
86
+       sodipodi:type="star"
87
+       style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
88
+       id="path2228"
89
+       sodipodi:sides="3"
90
+       sodipodi:cx="9.3702974"
91
+       sodipodi:cy="7.0801978"
92
+       sodipodi:r1="4.1024742"
93
+       sodipodi:r2="2.0512371"
94
+       sodipodi:arg1="0.54230274"
95
+       sodipodi:arg2="1.5895003"
96
+       inkscape:flatsided="true"
97
+       inkscape:rounded="0"
98
+       inkscape:randomized="0"
99
+       d="M 12.884159,9.1975246 L 5.779708,9.0646277 L 9.4470255,2.9784412 L 12.884159,9.1975246 z "
100
+       transform="matrix(0,0.8351333,-0.6431815,0,13.915678,1.2400099)" />
101
+  </g>
102
+</svg>

+ 102
- 0
src/com/dmdirc/addons/dcc/res/source/send.svg View File

@@ -0,0 +1,102 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://web.resource.org/cc/"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.45.1"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="send.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/trunk/src/com/dmdirc/res/server-normal.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874" />
24
+  <sodipodi:namedview
25
+     id="base"
26
+     pagecolor="#ffffff"
27
+     bordercolor="#666666"
28
+     borderopacity="1.0"
29
+     inkscape:pageopacity="0.0"
30
+     inkscape:pageshadow="2"
31
+     inkscape:zoom="22.197802"
32
+     inkscape:cx="10.049476"
33
+     inkscape:cy="9.9028618"
34
+     inkscape:current-layer="layer1"
35
+     showgrid="true"
36
+     inkscape:grid-bbox="true"
37
+     inkscape:document-units="px"
38
+     inkscape:window-width="849"
39
+     inkscape:window-height="581"
40
+     inkscape:window-x="195"
41
+     inkscape:window-y="150" />
42
+  <metadata
43
+     id="metadata1877">
44
+    <rdf:RDF>
45
+      <cc:Work
46
+         rdf:about="">
47
+        <dc:format>image/svg+xml</dc:format>
48
+        <dc:type
49
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
50
+      </cc:Work>
51
+    </rdf:RDF>
52
+  </metadata>
53
+  <g
54
+     id="layer1"
55
+     inkscape:label="Layer 1"
56
+     inkscape:groupmode="layer">
57
+    <rect
58
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:white"
59
+       id="rect4588"
60
+       width="11.064357"
61
+       height="9.19802"
62
+       x="2.5856435"
63
+       y="4.5044556" />
64
+    <rect
65
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
66
+       id="rect3671"
67
+       width="11.033788"
68
+       height="2.4977343"
69
+       x="2.4819307"
70
+       y="2.5022655" />
71
+    <rect
72
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
73
+       id="rect1880"
74
+       width="11.011163"
75
+       height="10.988637"
76
+       x="2.5394683"
77
+       y="2.5619929" />
78
+    <rect
79
+       style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
80
+       id="rect2226"
81
+       width="7.2534657"
82
+       height="2.9999995"
83
+       x="3.5"
84
+       y="7.5000005" />
85
+    <path
86
+       sodipodi:type="star"
87
+       style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
88
+       id="path2228"
89
+       sodipodi:sides="3"
90
+       sodipodi:cx="9.3702974"
91
+       sodipodi:cy="7.0801978"
92
+       sodipodi:r1="4.1024742"
93
+       sodipodi:r2="2.0512371"
94
+       sodipodi:arg1="0.54230274"
95
+       sodipodi:arg2="1.5895003"
96
+       inkscape:flatsided="true"
97
+       inkscape:rounded="0"
98
+       inkscape:randomized="0"
99
+       d="M 12.884159,9.1975246 L 5.779708,9.0646277 L 9.4470255,2.9784412 L 12.884159,9.1975246 z "
100
+       transform="matrix(0,0.8351333,-0.6431815,0,13.915678,1.2400099)" />
101
+  </g>
102
+</svg>

+ 126
- 0
src/com/dmdirc/addons/dcc/res/source/transfers.svg View File

@@ -0,0 +1,126 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+<svg
4
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
5
+   xmlns:cc="http://web.resource.org/cc/"
6
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7
+   xmlns:svg="http://www.w3.org/2000/svg"
8
+   xmlns="http://www.w3.org/2000/svg"
9
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
10
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
11
+   width="16px"
12
+   height="16px"
13
+   id="svg1872"
14
+   sodipodi:version="0.32"
15
+   inkscape:version="0.45.1"
16
+   sodipodi:docbase="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/source"
17
+   sodipodi:docname="transfers.svg"
18
+   inkscape:export-filename="/home/chris/DMDirc/src/com/dmdirc/addons/dcc/res/transfers.png"
19
+   inkscape:export-xdpi="90"
20
+   inkscape:export-ydpi="90"
21
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
22
+  <defs
23
+     id="defs1874" />
24
+  <sodipodi:namedview
25
+     id="base"
26
+     pagecolor="#ffffff"
27
+     bordercolor="#666666"
28
+     borderopacity="1.0"
29
+     inkscape:pageopacity="0.0"
30
+     inkscape:pageshadow="2"
31
+     inkscape:zoom="22.197802"
32
+     inkscape:cx="10.049476"
33
+     inkscape:cy="8.1008816"
34
+     inkscape:current-layer="layer1"
35
+     showgrid="true"
36
+     inkscape:grid-bbox="true"
37
+     inkscape:document-units="px"
38
+     inkscape:window-width="849"
39
+     inkscape:window-height="581"
40
+     inkscape:window-x="195"
41
+     inkscape:window-y="150" />
42
+  <metadata
43
+     id="metadata1877">
44
+    <rdf:RDF>
45
+      <cc:Work
46
+         rdf:about="">
47
+        <dc:format>image/svg+xml</dc:format>
48
+        <dc:type
49
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
50
+      </cc:Work>
51
+    </rdf:RDF>
52
+  </metadata>
53
+  <g
54
+     id="layer1"
55
+     inkscape:label="Layer 1"
56
+     inkscape:groupmode="layer">
57
+    <rect
58
+       style="fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill:white"
59
+       id="rect4588"
60
+       width="11.064357"
61
+       height="9.19802"
62
+       x="2.5856435"
63
+       y="4.5044556" />
64
+    <rect
65
+       style="fill:#ff0000;fill-opacity:1;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
66
+       id="rect3671"
67
+       width="11.033788"
68
+       height="2.4977343"
69
+       x="2.4819307"
70
+       y="2.5022655" />
71
+    <rect
72
+       style="fill:none;fill-opacity:1;stroke:black;stroke-width:0.8987385;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
73
+       id="rect1880"
74
+       width="11.011163"
75
+       height="10.988637"
76
+       x="2.5394683"
77
+       y="2.5619929" />
78
+    <rect
79
+       style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
80
+       id="rect2254"
81
+       width="4.9718494"
82
+       height="1"
83
+       x="6"
84
+       y="10" />
85
+    <path
86
+       sodipodi:type="star"
87
+       style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
88
+       id="path2256"
89
+       sodipodi:sides="3"
90
+       sodipodi:cx="9.3702974"
91
+       sodipodi:cy="7.0801978"
92
+       sodipodi:r1="4.1024742"
93
+       sodipodi:r2="2.0512371"
94
+       sodipodi:arg1="0.54230274"
95
+       sodipodi:arg2="1.5895003"
96
+       inkscape:flatsided="true"
97
+       inkscape:rounded="0"
98
+       inkscape:randomized="0"
99
+       d="M 12.884159,9.1975246 L 5.779708,9.0646277 L 9.4470255,2.9784412 L 12.884159,9.1975246 z "
100
+       transform="matrix(0,0.5630273,-0.5305009,0,13.580066,5.241411)" />
101
+    <rect
102
+       style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
103
+       id="rect2226"
104
+       width="5.1801977"
105
+       height="1"
106
+       x="-10"
107
+       y="6.9549503"
108
+       transform="scale(-1,1)" />
109
+    <path
110
+       sodipodi:type="star"
111
+       style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
112
+       id="path2228"
113
+       sodipodi:sides="3"
114
+       sodipodi:cx="9.3702974"
115
+       sodipodi:cy="7.0801978"
116
+       sodipodi:r1="4.1024742"
117
+       sodipodi:r2="2.0512371"
118
+       sodipodi:arg1="0.54230274"
119
+       sodipodi:arg2="1.5895003"
120
+       inkscape:flatsided="true"
121
+       inkscape:rounded="0"
122
+       inkscape:randomized="0"
123
+       d="M 12.884159,9.1975246 L 5.779708,9.0646277 L 9.4470255,2.9784412 L 12.884159,9.1975246 z "
124
+       transform="matrix(0,0.5630273,0.5305009,0,2.2397364,2.1602229)" />
125
+  </g>
126
+</svg>

BIN
src/com/dmdirc/addons/dcc/res/transfers.png View File


+ 82
- 0
src/com/dmdirc/addons/dcop/DcopCommand.java View File

@@ -0,0 +1,82 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcop;
24
+
25
+import com.dmdirc.Server;
26
+import com.dmdirc.commandparser.CommandArguments;
27
+import com.dmdirc.commandparser.CommandManager;
28
+import com.dmdirc.commandparser.commands.ServerCommand;
29
+import com.dmdirc.ui.interfaces.InputWindow;
30
+
31
+import java.util.List;
32
+
33
+/**
34
+ * The dcop command retrieves information from a dcop application.
35
+ * @author chris
36
+ */
37
+public final class DcopCommand extends ServerCommand {
38
+    
39
+    /**
40
+     * Creates a new instance of DcopCommand.
41
+     */
42
+    public DcopCommand() {
43
+        super();
44
+        
45
+        CommandManager.registerCommand(this);
46
+    }
47
+
48
+    /** {@inheritDoc} */
49
+    @Override
50
+    public void execute(final InputWindow origin, final Server server,
51
+            final boolean isSilent, final CommandArguments args) {
52
+        if (args.getArguments().length != 3) {
53
+            showUsage(origin, isSilent, "dcop", "<app> <object> <function>");
54
+            return;
55
+        }
56
+        
57
+        final List<String> res = DcopPlugin.getDcopResult("dcop " + args.getArgumentsAsString());
58
+        for (String line : res) {
59
+            sendLine(origin, isSilent, FORMAT_OUTPUT, line);
60
+        }
61
+    }
62
+    
63
+    
64
+    /** {@inheritDoc} */
65
+    @Override
66
+    public String getName() {
67
+        return "dcop";
68
+    }
69
+    
70
+    /** {@inheritDoc} */
71
+    @Override
72
+    public boolean showInHelp() {
73
+        return true;
74
+    }
75
+    
76
+    /** {@inheritDoc} */
77
+    @Override
78
+    public String getHelp() {
79
+        return "dcop <app> <object> <function> - retrieves information from a DCOP aplication";
80
+    }
81
+    
82
+}

+ 95
- 0
src/com/dmdirc/addons/dcop/DcopPlugin.java View File

@@ -0,0 +1,95 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dcop;
24
+
25
+import com.dmdirc.plugins.Plugin;
26
+import com.dmdirc.commandparser.CommandManager;
27
+
28
+import java.io.BufferedReader;
29
+import java.io.IOException;
30
+import java.io.InputStreamReader;
31
+import java.util.ArrayList;
32
+import java.util.List;
33
+
34
+/**
35
+ * Allows the user to execute dcop commands (and read the results).
36
+ * 
37
+ * @author chris
38
+ */
39
+public final class DcopPlugin extends Plugin {
40
+    /** The DcopCommand we created */
41
+    private DcopCommand command = null;
42
+    
43
+    /** Creates a new instance of DcopPlugin. */
44
+    public DcopPlugin() {
45
+        super();
46
+    }
47
+    
48
+    /**
49
+     * Retrieves the result from executing the specified command.
50
+     *
51
+     * @param command The command to be executed
52
+     * @return The output of the specified command
53
+     */
54
+    public static List<String> getDcopResult(final String command) {
55
+        final ArrayList<String> result = new ArrayList<String>();
56
+
57
+        InputStreamReader reader;
58
+        BufferedReader input;
59
+        Process process;
60
+        
61
+        try {
62
+            process = Runtime.getRuntime().exec(command);
63
+            
64
+            reader = new InputStreamReader(process.getInputStream());
65
+            input = new BufferedReader(reader);
66
+            
67
+            String line = "";
68
+            
69
+            while ((line = input.readLine()) != null) {
70
+                result.add(line);
71
+            }
72
+            
73
+            reader.close();
74
+            input.close();
75
+            process.destroy();
76
+        } catch (IOException ex) {
77
+            // Do nothing
78
+        }
79
+        
80
+        return result;
81
+    }
82
+    
83
+    /** {@inheritDoc} */
84
+    @Override
85
+    public void onLoad() {
86
+        command = new DcopCommand();
87
+    }
88
+    
89
+    /** {@inheritDoc} */
90
+    @Override
91
+    public void onUnload() {
92
+        CommandManager.unregisterCommand(command);
93
+    }
94
+
95
+}

+ 26
- 0
src/com/dmdirc/addons/dcop/package-info.java View File

@@ -0,0 +1,26 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+/**
24
+ * DCOP interface for DMDirc.
25
+ */
26
+package com.dmdirc.addons.dcop;

+ 33
- 0
src/com/dmdirc/addons/dcop/plugin.config View File

@@ -0,0 +1,33 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  updates
9
+  version
10
+
11
+metadata:
12
+  author=Chris <chris@dmdirc.com>
13
+  mainclass=com.dmdirc.addons.dcop.DcopPlugin
14
+  description=Provides commands to interface with DCOP applications
15
+  name=dcop
16
+  nicename=DCOP Plugin
17
+
18
+updates:
19
+  id=1
20
+
21
+version:
22
+  friendly=0.4
23
+
24
+persistent:
25
+
26
+provides:
27
+  dcop command
28
+
29
+required-services:
30
+#  linux os
31
+
32
+exports:
33
+  getDcopResult in com.dmdirc.addons.dcop.DcopPlugin as dcop

+ 90
- 0
src/com/dmdirc/addons/dns/DNSCommand.java View File

@@ -0,0 +1,90 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dns;
24
+
25
+import com.dmdirc.commandparser.CommandArguments;
26
+import com.dmdirc.commandparser.CommandManager;
27
+import com.dmdirc.commandparser.commands.GlobalCommand;
28
+import com.dmdirc.ui.interfaces.InputWindow;
29
+
30
+import java.util.Timer;
31
+import java.util.TimerTask;
32
+
33
+/**
34
+ * Performs DNS lookups for nicknames, hostnames or IPs.
35
+ */
36
+public final class DNSCommand extends GlobalCommand {
37
+    
38
+    /** Creates a new instance of DNSCommand. */
39
+    public DNSCommand() {
40
+        super();
41
+        
42
+        CommandManager.registerCommand(this);
43
+    }
44
+    
45
+    /** {@inheritDoc} */
46
+    @Override
47
+    public void execute(final InputWindow origin, final boolean isSilent,
48
+            final CommandArguments args) {
49
+        if (args.getArguments().length == 0) {
50
+            showUsage(origin, isSilent, "dns", "<IP|hostname>");
51
+            return;
52
+        }
53
+        
54
+        sendLine(origin, isSilent, FORMAT_OUTPUT, "Resolving: " + args.getArguments()[0]);
55
+        new Timer("DNS Command Timer").schedule(new TimerTask() {
56
+            /** {@inheritDoc} */
57
+            @Override
58
+            public void run() {
59
+                if (args.getArguments()[0].matches("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b")) {
60
+                    sendLine(origin, isSilent, FORMAT_OUTPUT, "Resolved: "
61
+                            + args.getArguments()[0] + ": "
62
+                            + DNSPlugin.getHostname(args.getArguments()[0]));
63
+                } else {
64
+                    sendLine(origin, isSilent, FORMAT_OUTPUT, "Resolved: "
65
+                            + args.getArguments()[0] + ": "
66
+                            + DNSPlugin.getIPs(args.getArguments()[0]));
67
+                }
68
+            }
69
+        }, 0);
70
+    }
71
+    
72
+    /** {@inheritDoc} */
73
+    @Override
74
+    public String getName() {
75
+        return "dns";
76
+    }
77
+    
78
+    /** {@inheritDoc} */
79
+    @Override
80
+    public boolean showInHelp() {
81
+        return true;
82
+    }
83
+    
84
+    /** {@inheritDoc} */
85
+    @Override
86
+    public String getHelp() {
87
+        return "dns <IP|hostname> - Performs DNS lookup of the specified ip/hostname/nickname";
88
+    }
89
+    
90
+}

+ 98
- 0
src/com/dmdirc/addons/dns/DNSPlugin.java View File

@@ -0,0 +1,98 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.dns;
24
+
25
+import com.dmdirc.commandparser.CommandManager;
26
+import com.dmdirc.plugins.Plugin;
27
+
28
+import java.net.InetAddress;
29
+import java.net.UnknownHostException;
30
+
31
+import java.util.ArrayList;
32
+import java.util.List;
33
+
34
+/**
35
+ * DNS plugin.
36
+ */
37
+public final class DNSPlugin extends Plugin {
38
+    
39
+    /** The DNSCommand we've registered. */
40
+    private DNSCommand command;
41
+    
42
+    /** Creates a new instance of DNSPlugin. */
43
+    public DNSPlugin() {
44
+        super();
45
+    }
46
+    
47
+    /** {@inheritDoc} */
48
+    @Override
49
+    public void onLoad() {
50
+        command = new DNSCommand();
51
+    }
52
+    
53
+    /** {@inheritDoc} */
54
+    @Override
55
+    public void onUnload() {
56
+        CommandManager.unregisterCommand(command);
57
+    }
58
+    
59
+    /**
60
+     * Returns the IP(s) for a hostname.
61
+     *
62
+     * @param hostname Hostname to resolve.
63
+     *
64
+     * @return Resolved IP(s)
65
+     */
66
+    public static String getIPs(final String hostname) {
67
+        List<String> results = new ArrayList<String>();
68
+        
69
+        try {
70
+            final InetAddress[] ips = InetAddress.getAllByName(hostname);
71
+            
72
+            for (InetAddress ip : ips) {
73
+                results.add(ip.getHostAddress());
74
+            }
75
+            
76
+        } catch (UnknownHostException ex) {
77
+            results = new ArrayList<String>();
78
+        }
79
+        
80
+        return results.toString();
81
+    }
82
+    
83
+    /**
84
+     * Returns the hostname for an ip.
85
+     *
86
+     * @param ip IP to resolve
87
+     *
88
+     * @return Resolved hostname
89
+     */
90
+    public static String getHostname(final String ip) {
91
+        try {
92
+            return InetAddress.getByName(ip).getHostName();
93
+        } catch (UnknownHostException ex) {
94
+            return "";
95
+        }
96
+    }
97
+    
98
+}

+ 26
- 0
src/com/dmdirc/addons/dns/package-info.java View File

@@ -0,0 +1,26 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+/**
24
+ * DNS plugin and command.
25
+ */
26
+package com.dmdirc.addons.dns;

+ 27
- 0
src/com/dmdirc/addons/dns/plugin.config View File

@@ -0,0 +1,27 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  updates
9
+  version
10
+
11
+metadata:
12
+  author=Greboid <greg@dmdirc.com>
13
+  mainclass=com.dmdirc.addons.dns.DNSPlugin
14
+  description=Provides a DNS command and method to the client
15
+  name=dns
16
+  nicename=DNS Plugin
17
+
18
+updates:
19
+  id=10
20
+
21
+version:
22
+  friendly=0.3
23
+
24
+provides:
25
+  dns command
26
+
27
+required-services:

+ 235
- 0
src/com/dmdirc/addons/identd/IdentClient.java View File

@@ -0,0 +1,235 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.identd;
24
+
25
+import com.dmdirc.Server;
26
+import com.dmdirc.ServerManager;
27
+import com.dmdirc.config.ConfigManager;
28
+import com.dmdirc.config.IdentityManager;
29
+import com.dmdirc.logger.ErrorLevel;
30
+import com.dmdirc.logger.Logger;
31
+
32
+import java.io.BufferedReader;
33
+import java.io.InputStreamReader;
34
+import java.io.IOException;
35
+import java.io.PrintWriter;
36
+import java.net.Socket;
37
+
38
+/**
39
+ * The IdentClient responds to an ident request.
40
+ *
41
+ * @author Shane "Dataforce" Mc Cormack
42
+ */
43
+public final class IdentClient implements Runnable {
44
+
45
+    /** The IdentdServer that owns this Client. */
46
+    private final IdentdServer myServer;
47
+
48
+    /** The Socket that we are in charge of. */
49
+    private final Socket mySocket;
50
+
51
+    /** The Thread in use for this client. */
52
+    private volatile Thread myThread;
53
+
54
+    /** The plugin that owns us. */
55
+    private final IdentdPlugin myPlugin;
56
+
57
+    /**
58
+     * Create the IdentClient.
59
+     *
60
+     * @param server The server that owns this
61
+     * @param socket The socket we are handing
62
+     */
63
+    public IdentClient(final IdentdServer server, final Socket socket, final IdentdPlugin plugin) {
64
+        myServer = server;
65
+        mySocket = socket;
66
+        myPlugin = plugin;
67
+
68
+        myThread = new Thread(this);
69
+        myThread.start();
70
+    }
71
+
72
+    /**
73
+     * Process this connection.
74
+     */
75
+    @Override
76
+    public void run() {
77
+        final Thread thisThread = Thread.currentThread();
78
+        PrintWriter out = null;
79
+        BufferedReader in = null;
80
+        try {
81
+            out = new PrintWriter(mySocket.getOutputStream(), true);
82
+            in = new BufferedReader(new InputStreamReader(mySocket.getInputStream()));
83
+            final String inputLine;
84
+            if ((inputLine = in.readLine()) != null) {
85
+                out.println(getIdentResponse(inputLine, IdentityManager.getGlobalConfig()));
86
+            }
87
+        } catch (IOException e) {
88
+            if (thisThread == myThread) {
89
+                Logger.userError(ErrorLevel.HIGH, "ClientSocket Error: " + e.getMessage());
90
+            }
91
+        } finally {
92
+            try {
93
+                out.close();
94
+                in.close();
95
+                mySocket.close();
96
+            } catch (IOException e) {
97
+            }
98
+        }
99
+        myServer.delClient(this);
100
+    }
101
+
102
+    /**
103
+     * Get the ident response for a given line.
104
+     * Complies with rfc1413 (http://www.faqs.org/rfcs/rfc1413.html)
105
+     *
106
+     * @param input Line to generate response for
107
+     * @param config The config manager to use for settings
108
+     * @return the ident response for the given line
109
+     */
110
+    protected String getIdentResponse(final String input, final ConfigManager config) {
111
+        final String unescapedInput = unescapeString(input);
112
+        final String[] bits = unescapedInput.replaceAll("\\s+", "").split(",", 2);
113
+        if (bits.length < 2) {
114
+            return String.format("%s : ERROR : X-INVALID-INPUT", escapeString(unescapedInput));
115
+        }
116
+        final int myPort;
117
+        final int theirPort;
118
+        try {
119
+            myPort = Integer.parseInt(bits[0].trim());
120
+            theirPort = Integer.parseInt(bits[1].trim());
121
+        } catch (NumberFormatException e) {
122
+            return String.format("%s , %s : ERROR : X-INVALID-INPUT", escapeString(bits[0]), escapeString(bits[1]));
123
+        }
124
+
125
+        if (myPort > 65535 || myPort < 1 || theirPort > 65535 || theirPort < 1) {
126
+            return String.format("%d , %d : ERROR : INVALID-PORT", myPort, theirPort);
127
+        }
128
+
129
+        final Server server = getServerByPort(myPort);
130
+        if (!config.getOptionBool(myPlugin.getDomain(), "advanced.alwaysOn") && (server == null || config.getOptionBool(myPlugin.getDomain(), "advanced.isNoUser"))) {
131
+            return String.format("%d , %d : ERROR : NO-USER", myPort, theirPort);
132
+        }
133
+
134
+        if (config.getOptionBool(myPlugin.getDomain(), "advanced.isHiddenUser")) {
135
+            return String.format("%d , %d : ERROR : HIDDEN-USER", myPort, theirPort);
136
+        }
137
+
138
+        final String osName = System.getProperty("os.name").toLowerCase();
139
+        final String os;
140
+        final String username;
141
+
142
+        final String customSystem = config.getOption(myPlugin.getDomain(), "advanced.customSystem");
143
+        if (config.getOptionBool(myPlugin.getDomain(), "advanced.useCustomSystem") && customSystem != null && customSystem.length() > 0 && customSystem.length() < 513) {
144
+            os = customSystem;
145
+        } else {
146
+            // Tad excessive maybe, but complete!
147
+            // Based on: http://mindprod.com/jgloss/properties.html
148
+            // and the SYSTEM NAMES section of rfc1340 (http://www.faqs.org/rfcs/rfc1340.html)
149
+            if (osName.startsWith("windows")) {
150
+                os = "WIN32";
151
+            } else if (osName.startsWith("mac")) {
152
+                os = "MACOS";
153
+            } else if (osName.startsWith("linux")) {
154
+                os = "UNIX";
155
+            } else if (osName.indexOf("bsd") > -1) {
156
+                os = "UNIX-BSD";
157
+            } else if ("os/2".equals(osName)) {
158
+                os = "OS/2";
159
+            } else if (osName.indexOf("unix") > -1) {
160
+                os = "UNIX";
161
+            } else if ("irix".equals(osName)) {
162
+                os = "IRIX";
163
+            } else {
164
+                os = "UNKNOWN";
165
+            }
166
+        }
167
+
168
+        final String customName = config.getOption(myPlugin.getDomain(), "general.customName");
169
+        if (config.getOptionBool(myPlugin.getDomain(), "general.useCustomName") && customName != null && customName.length() > 0 && customName.length() < 513) {
170
+            username = customName;
171
+        } else if (server != null && config.getOptionBool(myPlugin.getDomain(), "general.useNickname")) {
172
+            username = server.getParser().getLocalClient().getNickname();
173
+        } else if (server != null && config.getOptionBool(myPlugin.getDomain(), "general.useUsername")) {
174
+            username = server.getParser().getLocalClient().getUsername();
175
+        } else {
176
+            username = System.getProperty("user.name");
177
+        }
178
+
179
+        return String.format("%d , %d : USERID : %s : %s", myPort, theirPort, escapeString(os), escapeString(username));
180
+    }
181
+
182
+    /**
183
+     * Escape special chars.
184
+     *
185
+     * @param str String to escape
186
+     * @return Escaped string.
187
+     */
188
+    public static String escapeString(final String str) {
189
+        return str.replaceAll("\\\\", "\\\\\\\\").replaceAll(":", "\\\\:").replaceAll(",", "\\\\,").replaceAll(" ", "\\\\ ");
190
+    }
191
+
192
+    /**
193
+     * Unescape special chars.
194
+     *
195
+     * @param str String to escape
196
+     * @return Escaped string.
197
+     */
198
+    public static String unescapeString(final String str) {
199
+        return str.replaceAll("\\\\:", ":").replaceAll("\\\\ ", " ").replaceAll("\\\\,", ",").replaceAll("\\\\\\\\", "\\\\");
200
+    }
201
+
202
+    /**
203
+     * Close this IdentClient.
204
+     */
205
+    public void close() {
206
+        if (myThread != null) {
207
+            final Thread tmpThread = myThread;
208
+            myThread = null;
209
+            if (tmpThread != null) {
210
+                tmpThread.interrupt();
211
+            }
212
+            try {
213
+                mySocket.close();
214
+            } catch (IOException e) {
215
+            }
216
+        }
217
+    }
218
+
219
+    /**
220
+     * Retrieves the server that is bound to the specified local port.
221
+     *
222
+     * @param port Port to check for
223
+     * @return The server instance listening on the given port
224
+     */
225
+    protected static Server getServerByPort(final int port) {
226
+        for (Server server : ServerManager.getServerManager().getServers()) {
227
+            if (server.getParser().getLocalPort() == port) {
228
+                return server;
229
+            }
230
+        }
231
+        return null;
232
+    }
233
+
234
+}
235
+

+ 172
- 0
src/com/dmdirc/addons/identd/IdentdPlugin.java View File

@@ -0,0 +1,172 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.identd;
24
+
25
+import com.dmdirc.Server;
26
+import com.dmdirc.actions.ActionManager;
27
+import com.dmdirc.actions.CoreActionType;
28
+import com.dmdirc.actions.interfaces.ActionType;
29
+import com.dmdirc.config.IdentityManager;
30
+import com.dmdirc.config.prefs.PreferencesCategory;
31
+import com.dmdirc.config.prefs.PreferencesManager;
32
+import com.dmdirc.config.prefs.PreferencesSetting;
33
+import com.dmdirc.config.prefs.PreferencesType;
34
+import com.dmdirc.config.prefs.validator.PortValidator;
35
+import com.dmdirc.interfaces.ActionListener;
36
+import com.dmdirc.plugins.Plugin;
37
+
38
+import java.util.ArrayList;
39
+import java.util.List;
40
+
41
+/**
42
+ * The Identd plugin answers ident requests from IRC servers.
43
+ *
44
+ * @author Shane
45
+ */
46
+public class IdentdPlugin extends Plugin implements ActionListener {
47
+
48
+    /** Array list to store all the servers in that need ident replies. */
49
+    private final List<Server> servers = new ArrayList<Server>();
50
+
51
+    /** The IdentdServer that we use. */
52
+    private IdentdServer myServer;
53
+
54
+    /**
55
+     * Creates a new instance of IdentdPlugin.
56
+     */
57
+    public IdentdPlugin() {
58
+    }
59
+
60
+    /**
61
+     * Called when the plugin is loaded.
62
+     */
63
+    @Override
64
+    public void onLoad() {
65
+        // Add action hooks
66
+        ActionManager.addListener(this, CoreActionType.SERVER_CONNECTED, CoreActionType.SERVER_CONNECTING, CoreActionType.SERVER_CONNECTERROR);
67
+
68
+        myServer = new IdentdServer(this);
69
+        if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "advanced.alwaysOn")) {
70
+            myServer.startServer();
71
+        }
72
+    }
73
+
74
+    /**
75
+     * Called when this plugin is unloaded.
76
+     */
77
+    @Override
78
+    public void onUnload() {
79
+        myServer.stopServer();
80
+        servers.clear();
81
+        ActionManager.removeListener(this);
82
+    }
83
+
84
+    /**
85
+     * Process an event of the specified type.
86
+     *
87
+     * @param type The type of the event to process
88
+     * @param format Format of messages that are about to be sent. (May be null)
89
+     * @param arguments The arguments for the event
90
+     */
91
+    @Override
92
+    public void processEvent(final ActionType type, final StringBuffer format, final Object... arguments) {
93
+        if (type == CoreActionType.SERVER_CONNECTING) {
94
+            synchronized (servers) {
95
+                if (servers.isEmpty()) {
96
+                    myServer.startServer();
97
+                }
98
+                servers.add((Server) arguments[0]);
99
+            }
100
+        } else if (type == CoreActionType.SERVER_CONNECTED || type == CoreActionType.SERVER_CONNECTERROR) {
101
+            synchronized (servers) {
102
+                servers.remove(arguments[0]);
103
+
104
+                if (servers.isEmpty() && !IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "advanced.alwaysOn")) {
105
+                    myServer.stopServer();
106
+                }
107
+            }
108
+        }
109
+    }
110
+
111
+    /** {@inheritDoc} */
112
+    @Override
113
+    public void showConfig(final PreferencesManager manager) {
114
+        final PreferencesCategory general = new PreferencesCategory("Identd",
115
+                "General Identd Plugin config ('Lower' options take priority " +
116
+                "over those above them)");
117
+        final PreferencesCategory advanced = new PreferencesCategory("Advanced",
118
+                "Advanced Identd Plugin config - Only edit these if you need " +
119
+                "to/know what you are doing. Editing these could prevent " +
120
+                "access to some servers. ('Lower' options take priority over " +
121
+                "those above them)");
122
+
123
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
124
+                getDomain(), "general.useUsername", "Use connection " +
125
+                "username rather than system username", "If this is enabled," +
126
+                " the username for the connection will be used rather than " +
127
+                "'" + System.getProperty("user.name") + "'"));
128
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
129
+                getDomain(), "general.useNickname", "Use connection " +
130
+                "nickname rather than system username", "If this is enabled, " +
131
+                "the nickname for the connection will be used rather than " +
132
+                "'" + System.getProperty("user.name") + "'"));
133
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
134
+                getDomain(), "general.useCustomName", "Use custom name" +
135
+                " all the time", "If this is enabled, the name specified below" +
136
+                " will be used all the time"));
137
+        general.addSetting(new PreferencesSetting(PreferencesType.TEXT,
138
+                getDomain(), "general.customName", "Custom Name to use",
139
+                "The custom name to use when 'Use Custom Name' is enabled"));
140
+
141
+        advanced.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
142
+                getDomain(), "advanced.alwaysOn", "Always have ident " +
143
+                "port open", "By default the identd only runs when there are " +
144
+                "active connection attempts. This overrides that."));
145
+        advanced.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
146
+                new PortValidator(), getDomain(), "advanced.port",
147
+                "What port should the identd listen on", "Default port is 113," +
148
+                " this is probably useless if changed unless you port forward" +
149
+                " ident to a different port"));
150
+        advanced.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
151
+                getDomain(), "advanced.useCustomSystem", "Use custom OS",
152
+                "By default the plugin uses 'UNIX' or 'WIN32' as the system " +
153
+                "type, this can be overriden by enabling this."));
154
+        advanced.addSetting(new PreferencesSetting(PreferencesType.TEXT,
155
+                getDomain(), "advanced.customSystem", "Custom OS to use",
156
+                "The custom system to use when 'Use Custom System' is enabled"));
157
+        advanced.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
158
+                getDomain(), "advanced.isHiddenUser", "Respond to ident" +
159
+                " requests with HIDDEN-USER error", "By default the plugin will" +
160
+                " give a USERID response, this can force an 'ERROR :" +
161
+                " HIDDEN-USER' response instead."));
162
+        advanced.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
163
+                getDomain(), "advanced.isNoUser", "Respond to ident" +
164
+                " requests with NO-USER error", "By default the plugin will" +
165
+                " give a USERID response, this can force an 'ERROR : NO-USER'" +
166
+                " response instead. (Overrides HIDDEN-USER)"));
167
+
168
+        manager.getCategory("Plugins").addSubCategory(general);
169
+        general.addSubCategory(advanced);
170
+    }
171
+
172
+}

+ 168
- 0
src/com/dmdirc/addons/identd/IdentdServer.java View File

@@ -0,0 +1,168 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.identd;
24
+
25
+import com.dmdirc.config.IdentityManager;
26
+import com.dmdirc.logger.Logger;
27
+import com.dmdirc.logger.ErrorLevel;
28
+import com.dmdirc.plugins.PluginInfo;
29
+import com.dmdirc.plugins.PluginManager;
30
+
31
+import java.net.Socket;
32
+import java.net.ServerSocket;
33
+import java.io.IOException;
34
+import java.util.ArrayList;
35
+import java.util.List;
36
+
37
+/**
38
+ * The IdentdServer watches over the ident port when required
39
+ *
40
+ * @author Shane "Dataforce" Mc Cormack
41
+ */
42
+public final class IdentdServer implements Runnable {
43
+
44
+    /** The Thread in use for this server */
45
+    private volatile Thread myThread = null;
46
+
47
+    /** The current socket in use for this server */
48
+    private ServerSocket serverSocket;
49
+
50
+    /** Arraylist of all the clients we have */
51
+    private final List<IdentClient> clientList = new ArrayList<IdentClient>();
52
+
53
+    /** The plugin that owns us. */
54
+    private final IdentdPlugin myPlugin;
55
+
56
+    /**
57
+     * Create the IdentdServer.
58
+     */
59
+    public IdentdServer(final IdentdPlugin plugin) {
60
+        super();
61
+        myPlugin = plugin;
62
+    }
63
+
64
+    /**
65
+     * Run this IdentdServer.
66
+     */
67
+    @Override
68
+    public void run() {
69
+        final Thread thisThread = Thread.currentThread();
70
+        while (myThread == thisThread) {
71
+            try {
72
+                final Socket clientSocket = serverSocket.accept();
73
+                final IdentClient client = new IdentClient(this, clientSocket, myPlugin);
74
+                addClient(client);
75
+            } catch (IOException e) {
76
+                if (myThread == thisThread) {
77
+                    Logger.userError(ErrorLevel.HIGH, "Accepting client failed: " + e.getMessage());
78
+                }
79
+            }
80
+        }
81
+    }
82
+
83
+    /**
84
+     * Add an IdentClient to the clientList
85
+     *
86
+     * @param client Client to add
87
+     */
88
+    public void addClient(final IdentClient client) {
89
+        synchronized (clientList) {
90
+            clientList.add(client);
91
+        }
92
+    }
93
+
94
+    /**
95
+     * Remove an IdentClient from the clientList
96
+     *
97
+     * @param client Client to remove
98
+     */
99
+    public void delClient(final IdentClient client) {
100
+        synchronized (clientList) {
101
+            for (int i = 0; i < clientList.size(); ++i) {
102
+                if (clientList.get(i) == client) {
103
+                    clientList.remove(i);
104
+                    break;
105
+                }
106
+            }
107
+        }
108
+    }
109
+
110
+    /**
111
+     * Check if the server is currently running
112
+     *
113
+     * @return True if the server is running
114
+     */
115
+    public boolean isRunning() {
116
+        return (myThread != null);
117
+    }
118
+
119
+    /**
120
+     * Start the ident server
121
+     */
122
+    public void startServer() {
123
+        if (myThread == null) {
124
+            try {
125
+                final int identPort = IdentityManager.getGlobalConfig().getOptionInt(myPlugin.getDomain(), "advanced.port");
126
+                serverSocket = new ServerSocket(identPort);
127
+                myThread = new Thread(this);
128
+                myThread.start();
129
+            } catch (IOException e) {
130
+                Logger.userError(ErrorLevel.MEDIUM, "Unable to start identd server: " + e.getMessage());
131
+                if (e.getMessage().equals("Permission denied")) {
132
+                    final PluginInfo plugin = PluginManager.getPluginManager().getPluginInfoByName("identd");
133
+                    if (plugin != null) {
134
+                        if (PluginManager.getPluginManager().delPlugin(plugin.getRelativeFilename())) {
135
+                            PluginManager.getPluginManager().updateAutoLoad(plugin);
136
+                        }
137
+                    }
138
+                }
139
+            }
140
+        }
141
+    }
142
+
143
+    /**
144
+     * Stop the ident server
145
+     */
146
+    public void stopServer() {
147
+        if (myThread != null) {
148
+            final Thread tmpThread = myThread;
149
+            myThread = null;
150
+            if (tmpThread != null) {
151
+                tmpThread.interrupt();
152
+            }
153
+            try {
154
+                serverSocket.close();
155
+            } catch (IOException e) {
156
+            }
157
+
158
+            synchronized (clientList) {
159
+                for (int i = 0; i < clientList.size(); ++i) {
160
+                    clientList.get(i).close();
161
+                }
162
+                clientList.clear();
163
+            }
164
+        }
165
+    }
166
+
167
+}
168
+

+ 41
- 0
src/com/dmdirc/addons/identd/plugin.config View File

@@ -0,0 +1,41 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  updates
9
+  defaults
10
+  version
11
+
12
+metadata:
13
+  author=Shane <shane@dmdirc.com>
14
+  mainclass=com.dmdirc.addons.identd.IdentdPlugin
15
+  description=Answers ident requests from IRC servers
16
+  name=identd
17
+  nicename=Identd Plugin
18
+
19
+updates:
20
+  id=15
21
+
22
+version:
23
+  friendly=0.3
24
+
25
+provides:
26
+  ident feature
27
+
28
+required-services:
29
+
30
+defaults:
31
+  general.useUsername=false
32
+  general.useNickname=false
33
+  general.useCustomName=false
34
+  general.customName=DMDirc-user
35
+
36
+  advanced.alwaysOn=false
37
+  advanced.port=113
38
+  advanced.useCustomSystem=false
39
+  advanced.customSystem=OTHER
40
+  advanced.isHiddenUser=false
41
+  advanced.isNoUser=false

+ 63
- 0
src/com/dmdirc/addons/lagdisplay/LagDisplayPanel.java View File

@@ -0,0 +1,63 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+package com.dmdirc.addons.lagdisplay;
23
+
24
+import com.dmdirc.addons.ui_swing.components.statusbar.StatusbarPopupPanel;
25
+import com.dmdirc.addons.ui_swing.components.statusbar.StatusbarPopupWindow;
26
+
27
+/**
28
+ * Shows the user's lag in the status bar, and reveals details of all servers
29
+ * when the user hovers over it.
30
+ * 
31
+ * @author chris
32
+ */
33
+public class LagDisplayPanel extends StatusbarPopupPanel {
34
+
35
+    /**
36
+     * A version number for this class. It should be changed whenever the class
37
+     * structure is changed (or anything else that would prevent serialized
38
+     * objects being unserialized with the new class).
39
+     */
40
+    private static final long serialVersionUID = 2;
41
+
42
+    /** Lag display plugin. */
43
+    private LagDisplayPlugin plugin;
44
+
45
+    /**
46
+     * Creates a new {@link LagDisplayPanel} for the specified plugin.
47
+     *
48
+     * @param plugin The plugin that owns this panel
49
+     */
50
+    public LagDisplayPanel(final LagDisplayPlugin plugin) {
51
+        super();
52
+        
53
+        this.plugin = plugin;
54
+    }
55
+
56
+    /** {@inheritDoc} */
57
+    @Override
58
+    protected StatusbarPopupWindow getWindow() {
59
+        return new ServerInfoDialog(plugin, this);
60
+    }
61
+    
62
+  
63
+}

+ 285
- 0
src/com/dmdirc/addons/lagdisplay/LagDisplayPlugin.java View File

@@ -0,0 +1,285 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.lagdisplay;
24
+
25
+import com.dmdirc.FrameContainer;
26
+import com.dmdirc.Main;
27
+import com.dmdirc.Server;
28
+import com.dmdirc.ServerState;
29
+import com.dmdirc.actions.ActionManager;
30
+import com.dmdirc.actions.interfaces.ActionType;
31
+import com.dmdirc.actions.CoreActionType;
32
+import com.dmdirc.config.ConfigManager;
33
+import com.dmdirc.config.IdentityManager;
34
+import com.dmdirc.config.prefs.PreferencesCategory;
35
+import com.dmdirc.config.prefs.PreferencesManager;
36
+import com.dmdirc.config.prefs.PreferencesSetting;
37
+import com.dmdirc.config.prefs.PreferencesType;
38
+import com.dmdirc.interfaces.ActionListener;
39
+import com.dmdirc.interfaces.ConfigChangeListener;
40
+import com.dmdirc.plugins.Plugin;
41
+import com.dmdirc.ui.interfaces.Window;
42
+import com.dmdirc.util.RollingList;
43
+
44
+import java.util.Date;
45
+import java.util.HashMap;
46
+import java.util.Map;
47
+import java.util.WeakHashMap;
48
+
49
+/**
50
+ * Displays the current server's lag in the status bar.
51
+ * @author chris
52
+ */
53
+public final class LagDisplayPlugin extends Plugin implements ActionListener, ConfigChangeListener {
54
+    
55
+    /** The panel we use in the status bar. */
56
+    private final LagDisplayPanel panel = new LagDisplayPanel(this);
57
+    
58
+    /** A cache of ping times. */
59
+    private final Map<Server, String> pings = new WeakHashMap<Server, String>();
60
+
61
+    /** Ping history. */
62
+    private final Map<Server, RollingList<Long>> history
63
+            = new HashMap<Server, RollingList<Long>>();
64
+
65
+    /** Whether or not to show a graph in the info popup. */
66
+    private boolean showGraph = true;
67
+
68
+    /** Whether or not to show labels on that graph. */
69
+    private boolean showLabels = true;
70
+
71
+    /** The length of history to keep per-server. */
72
+    private int historySize = 100;
73
+    
74
+    /** Creates a new instance of LagDisplayPlugin. */
75
+    public LagDisplayPlugin() {
76
+        super();
77
+    }
78
+    
79
+    /** {@inheritDoc} */
80
+    @Override
81
+    public void onLoad() {
82
+        Main.getUI().getStatusBar().addComponent(panel);
83
+        IdentityManager.getGlobalConfig().addChangeListener(getDomain(), this);
84
+
85
+        readConfig();
86
+        
87
+        ActionManager.addListener(this, CoreActionType.SERVER_GOTPING,
88
+                CoreActionType.SERVER_NOPING, CoreActionType.CLIENT_FRAME_CHANGED,
89
+                CoreActionType.SERVER_DISCONNECTED, CoreActionType.SERVER_PINGSENT,
90
+                CoreActionType.SERVER_NUMERIC);
91
+    }
92
+
93
+    /**
94
+     * Reads the plugin's global configuration settings.
95
+     */
96
+    protected void readConfig() {
97
+        final ConfigManager manager = IdentityManager.getGlobalConfig();
98
+        showGraph = manager.getOptionBool(getDomain(), "graph");
99
+        showLabels = manager.getOptionBool(getDomain(), "labels");
100
+        historySize = manager.getOptionInt(getDomain(), "history");
101
+    }
102
+
103
+    /**
104
+     * Retrieves the history of the specified server. If there is no history,
105
+     * a new list is added to the history map and returned.
106
+     * 
107
+     * @param server The server whose history is being requested
108
+     * @return The history for the specified server
109
+     */
110
+    protected RollingList<Long> getHistory(final Server server) {
111
+        if (!history.containsKey(server)) {
112
+            history.put(server, new RollingList<Long>(historySize));
113
+        }
114
+
115
+        return history.get(server);
116
+    }
117
+
118
+    /**
119
+     * Determines if the {@link ServerInfoDialog} should show a graph of the
120
+     * ping time for the current server.
121
+     * 
122
+     * @return True if a graph should be shown, false otherwise
123
+     */
124
+    public boolean shouldShowGraph() {
125
+        return showGraph;
126
+    }
127
+
128
+    /**
129
+     * Determines if the {@link PingHistoryPanel} should show labels on selected
130
+     * points.
131
+     *
132
+     * @return True if labels should be shown, false otherwise
133
+     */
134
+    public boolean shouldShowLabels() {
135
+        return showLabels;
136
+    }
137
+    
138
+    /** {@inheritDoc} */
139
+    @Override
140
+    public void onUnload() {
141
+        Main.getUI().getStatusBar().removeComponent(panel);
142
+        IdentityManager.getConfigIdentity().removeListener(this);
143
+        
144
+        ActionManager.removeListener(this);
145
+    }
146
+    
147
+    /** {@inheritDoc} */
148
+    @Override
149
+    public void processEvent(final ActionType type, final StringBuffer format,
150
+            final Object... arguments) {
151
+        boolean useAlternate = false;
152
+
153
+        for (Object obj : arguments) {
154
+            if (obj instanceof FrameContainer
155
+                    && ((FrameContainer) obj).getConfigManager() != null) {
156
+                useAlternate = ((FrameContainer) obj).getConfigManager()
157
+                        .getOptionBool(getDomain(), "usealternate");
158
+                break;
159
+            }
160
+        }
161
+
162
+        if (!useAlternate && type.equals(CoreActionType.SERVER_GOTPING)) {
163
+            final Window active = Main.getUI().getActiveWindow();
164
+            final String value = formatTime(arguments[1]);
165
+
166
+            getHistory(((Server) arguments[0])).add((Long) arguments[1]);
167
+            pings.put(((Server) arguments[0]), value);
168
+
169
+            if (((Server) arguments[0]).ownsFrame(active)) {
170
+                panel.setText(value);
171
+            }
172
+
173
+            panel.refreshDialog();
174
+        } else if (!useAlternate && type.equals(CoreActionType.SERVER_NOPING)) {
175
+            final Window active = Main.getUI().getActiveWindow();
176
+            final String value = formatTime(arguments[1]) + "+";
177
+
178
+            pings.put(((Server) arguments[0]), value);
179
+
180
+            if (((Server) arguments[0]).ownsFrame(active)) {
181
+                panel.setText(value);
182
+            }
183
+
184
+            panel.refreshDialog();
185
+        } else if (type.equals(CoreActionType.SERVER_DISCONNECTED)) {
186
+            final Window active = Main.getUI().getActiveWindow();
187
+
188
+            if (((Server) arguments[0]).ownsFrame(active)) {
189
+                panel.setText("Not connected");
190
+                pings.remove(arguments[0]);
191
+            }
192
+
193
+            panel.refreshDialog();
194
+        } else if (type.equals(CoreActionType.CLIENT_FRAME_CHANGED)) {
195
+            final FrameContainer source = (FrameContainer) arguments[0];
196
+            if (source.getServer() == null) {
197
+                panel.setText("Unknown");
198
+            } else if (source.getServer().getState() != ServerState.CONNECTED) {
199
+                panel.setText("Not connected");
200
+            } else {
201
+                panel.setText(getTime(source.getServer()));
202
+            }
203
+
204
+            panel.refreshDialog();
205
+        } else if (useAlternate && type.equals(CoreActionType.SERVER_PINGSENT)) {
206
+            ((Server) arguments[0]).getParser().sendRawMessage("LAGCHECK_" + new Date().getTime());
207
+        } else if (useAlternate && type.equals(CoreActionType.SERVER_NUMERIC)
208
+                && ((Integer) arguments[1]).intValue() == 421
209
+                && ((String[]) arguments[2])[3].startsWith("LAGCHECK_")) {
210
+            try {
211
+                final long sent = Long.parseLong(((String[]) arguments[2])[3].substring(9));
212
+                final Long duration = Long.valueOf(new Date().getTime() - sent);
213
+                final String value = formatTime(duration);
214
+                final Window active = Main.getUI().getActiveWindow();
215
+
216
+                pings.put((Server) arguments[0], value);
217
+                getHistory(((Server) arguments[0])).add(duration);
218
+
219
+                if (((Server) arguments[0]).ownsFrame(active)) {
220
+                    panel.setText(value);
221
+                }
222
+            } catch (NumberFormatException ex) {
223
+                pings.remove((Server) arguments[0]);
224
+            }
225
+
226
+            if (format != null) {
227
+                format.delete(0, format.length());
228
+            }
229
+
230
+            panel.refreshDialog();
231
+        }
232
+    }
233
+
234
+    /**
235
+     * Retrieves the ping time for the specified server.
236
+     *
237
+     * @param server The server whose ping time is being requested
238
+     * @return A String representation of the current lag, or "Unknown"
239
+     */
240
+    public String getTime(final Server server) {
241
+        return pings.get(server) == null ? "Unknown" : pings.get(server);
242
+    }
243
+    
244
+    /**
245
+     * Formats the specified time so it's a nice size to display in the label.
246
+     * @param object An uncast Long representing the time to be formatted
247
+     * @return Formatted time string
248
+     */
249
+    protected String formatTime(final Object object) {
250
+        final Long time = (Long) object;
251
+        
252
+        if (time >= 10000) {
253
+            return Math.round(time / 1000.0) + "s";
254
+        } else {
255
+            return time + "ms";
256
+        }
257
+    }
258
+
259
+    /** {@inheritDoc} */
260
+    @Override
261
+    public void showConfig(final PreferencesManager manager) {
262
+        final PreferencesCategory cat = new PreferencesCategory("Lag display plugin",
263
+                                                                "");
264
+        cat.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
265
+                getDomain(), "usealternate",
266
+                "Alternate method", "Use an alternate method of determining "
267
+                + "lag which bypasses bouncers or proxies that may reply?"));
268
+        cat.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
269
+                getDomain(), "graph", "Show graph", "Show a graph of ping times " +
270
+                "for the current server in the information popup?"));
271
+        cat.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
272
+                getDomain(), "labels", "Show labels", "Show labels on selected " +
273
+                "points on the ping graph?"));
274
+        cat.addSetting(new PreferencesSetting(PreferencesType.INTEGER,
275
+                getDomain(), "history", "Graph points", "Number of data points " +
276
+                "to plot on the graph, if enabled."));
277
+        manager.getCategory("Plugins").addSubCategory(cat);
278
+    }
279
+
280
+    /** {@inheritDoc} */
281
+    @Override
282
+    public void configChanged(final String domain, final String key) {
283
+        readConfig();
284
+    }
285
+}

+ 176
- 0
src/com/dmdirc/addons/lagdisplay/PingHistoryPanel.java View File

@@ -0,0 +1,176 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.lagdisplay;
24
+
25
+import com.dmdirc.Main;
26
+import com.dmdirc.ServerManager;
27
+import com.dmdirc.util.RollingList;
28
+import java.awt.Color;
29
+import java.awt.Dimension;
30
+import java.awt.Graphics;
31
+import java.awt.Graphics2D;
32
+import java.awt.Rectangle;
33
+import java.awt.geom.Rectangle2D;
34
+import java.util.ArrayList;
35
+import java.util.List;
36
+import javax.swing.JPanel;
37
+
38
+/**
39
+ * Shows a ping history graph for the current server.
40
+ *
41
+ * @author chris
42
+ */
43
+public class PingHistoryPanel extends JPanel {
44
+
45
+    /**
46
+     * A version number for this class. It should be changed whenever the class
47
+     * structure is changed (or anything else that would prevent serialized
48
+     * objects being unserialized with the new class).
49
+     */
50
+    private static final long serialVersionUID = 1;
51
+
52
+    /** The plugin that this panel is for. */
53
+    protected final LagDisplayPlugin plugin;
54
+
55
+    /** The history that we're graphing. */
56
+    protected final RollingList<Long> history;
57
+
58
+    /** The maximum ping value. */
59
+    protected long maximum = 0l;
60
+
61
+    /**
62
+     * Creates a new history panel for the specified plugin.
63
+     *
64
+     * @param plugin The plugin that owns this panel
65
+     */
66
+    public PingHistoryPanel(final LagDisplayPlugin plugin) {
67
+        super();
68
+
69
+        setMinimumSize(new Dimension(50, 100));
70
+        setMaximumSize(new Dimension(Integer.MAX_VALUE, 100));
71
+        setOpaque(false);
72
+        
73
+        this.plugin = plugin;
74
+        this.history = plugin.getHistory(ServerManager.getServerManager()
75
+                .getServerFromFrame(Main.getUI().getActiveWindow()));
76
+
77
+        for (Long value : history.getList()) {
78
+            maximum = Math.max(value, maximum);
79
+        }
80
+    }
81
+
82
+    /** {@inheritDoc} */
83
+    @Override
84
+    public void paint(final Graphics g) {
85
+        super.paint(g);
86
+        
87
+        g.setColor(Color.DARK_GRAY);
88
+        g.drawLine(2, 1, 2, getHeight() - 1);
89
+        g.drawLine(1, getHeight() - 2, getWidth() - 1, getHeight() - 2);
90
+        g.setFont(g.getFont().deriveFont(10f));
91
+
92
+        float lastX = -1, lastY = -1;
93
+        float pixelsperpointX = (getWidth() - 3) / (float) (history.getList().size() == 1 ? 1
94
+                : history.getList().size() - 1);
95
+        float pixelsperpointY = (getHeight() - 10) / (float) maximum;
96
+
97
+        if (history.isEmpty()) {
98
+            g.drawString("No data", getWidth() / 2 - 25, getHeight() / 2 + 5);
99
+        }
100
+
101
+        long last1 = -1, last2 = -1;
102
+        final List<Long> list = history.getList();
103
+        final List<Rectangle> rects = new ArrayList<Rectangle>();
104
+
105
+        for (int i = 0; i < list.size(); i++) {
106
+            final Long value = list.get(i);
107
+           
108
+            float x = lastX == -1 ? 2 : lastX + pixelsperpointX;
109
+            float y = getHeight() - 5 - value * pixelsperpointY;
110
+
111
+            if (lastX > -1) {
112
+                g.drawLine((int) lastX, (int) lastY, (int) x, (int) y);
113
+            }
114
+
115
+            g.drawRect((int) x - 1, (int) y - 1, 2, 2);
116
+
117
+            if (plugin.shouldShowLabels() && last1 > -1 && (last2 <= last1 || last1 >= value)) {
118
+                final String text = plugin.formatTime(last1);
119
+                final Rectangle2D rect = g.getFont().getStringBounds(text,
120
+                        ((Graphics2D) g).getFontRenderContext());
121
+                final int width = 10 + (int) rect.getWidth();
122
+                final int points = (int) Math.ceil(width / pixelsperpointX);
123
+                final int diffy = (int) (last1 - (10 + rect.getHeight()) / pixelsperpointY);
124
+
125
+                float posX = lastX - width + 7;
126
+                float posY = (float) (lastY + rect.getHeight() / 2) - 1;
127
+                boolean failed = posX < 0;
128
+
129
+                // Check left
130
+                for (int j = Math.max(0, i - points); j < i - 1; j++) {
131
+                    if (list.get(j) > diffy) {
132
+                        failed = true;
133
+                        break;
134
+                    }
135
+                }
136
+
137
+                if (failed) {
138
+                    posX = lastX + 3;
139
+                    failed = posX + width > getWidth();
140
+
141
+                    // Check right
142
+                    for (int j = i; j < Math.min(list.size(), i + points); j++) {
143
+                        if (list.get(j) > diffy) {
144
+                            failed = true;
145
+                            break;
146
+                        }
147
+                    }
148
+                }
149
+
150
+                if (!failed) {
151
+                    final Rectangle myrect = new Rectangle((int) posX - 2,
152
+                            (int) lastY - 7, 5 + (int) rect.getWidth(),
153
+                            3 + (int) rect.getHeight());
154
+
155
+                    for (Rectangle test : rects) {
156
+                        if (test.intersects(myrect)) {
157
+                            failed = true;
158
+                        }
159
+                    }
160
+
161
+                    if (!failed) {
162
+                        g.drawString(text, (int) posX, (int) posY);
163
+                        rects.add(myrect);
164
+                    }
165
+                }
166
+            }
167
+
168
+            lastX = x;
169
+            lastY = y;
170
+
171
+            last2 = last1;
172
+            last1 = value;
173
+        }
174
+    }
175
+
176
+}

+ 93
- 0
src/com/dmdirc/addons/lagdisplay/ServerInfoDialog.java View File

@@ -0,0 +1,93 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.lagdisplay;
24
+
25
+import com.dmdirc.Main;
26
+import com.dmdirc.Server;
27
+import com.dmdirc.ServerManager;
28
+import com.dmdirc.ServerState;
29
+import com.dmdirc.addons.ui_swing.MainFrame;
30
+import com.dmdirc.addons.ui_swing.components.statusbar.StatusbarPopupPanel;
31
+import com.dmdirc.addons.ui_swing.components.statusbar.StatusbarPopupWindow;
32
+
33
+import java.util.List;
34
+
35
+import javax.swing.JLabel;
36
+import javax.swing.JPanel;
37
+import javax.swing.JSeparator;
38
+
39
+
40
+/**
41
+ * Shows information about all connected servers.
42
+ *
43
+ * @author chris
44
+ */
45
+public class ServerInfoDialog extends StatusbarPopupWindow {
46
+
47
+    /**
48
+     * A version number for this class. It should be changed whenever the class
49
+     * structure is changed (or anything else that would prevent serialized
50
+     * objects being unserialized with the new class).
51
+     */
52
+    private static final long serialVersionUID = 3;
53
+
54
+    /** The lag display plugin. */
55
+    protected final LagDisplayPlugin plugin;
56
+
57
+    /**
58
+     * Creates a new ServerInfoDialog.
59
+     *
60
+     * @param ldp The {@link LagDisplayPlugin} we're using for info
61
+     * @param parent The {@link JPanel} to use for positioning
62
+     */
63
+    public ServerInfoDialog(final LagDisplayPlugin ldp, final StatusbarPopupPanel parent) {
64
+        super(parent, (MainFrame) Main.getUI().getMainWindow());
65
+
66
+        this.plugin = ldp;
67
+    }
68
+
69
+    /** {@inheritDoc} */
70
+    @Override
71
+    protected void initContent(final JPanel panel) {
72
+        final List<Server> servers = ServerManager.getServerManager().getServers();
73
+
74
+        if (servers.isEmpty()) {
75
+            panel.add(new JLabel("No open servers."));
76
+        } else {
77
+            if (plugin.shouldShowGraph()) {
78
+                panel.add(new PingHistoryPanel(plugin), "span, grow, wrap");
79
+                panel.add(new JSeparator(), "span, grow, wrap");
80
+            }
81
+
82
+            for (Server server : servers) {
83
+                panel.add(new JLabel(server.getName()));
84
+                panel.add(new JLabel(server.getState() == ServerState.CONNECTED
85
+                        ? server.getNetwork() : "---", JLabel.CENTER), "grow");
86
+                panel.add(new JLabel(server.getState() == ServerState.CONNECTED
87
+                        ? plugin.getTime(server) : "---", JLabel.RIGHT), "grow, wrap");
88
+            }
89
+        }
90
+    }
91
+
92
+
93
+}

+ 26
- 0
src/com/dmdirc/addons/lagdisplay/package-info.java View File

@@ -0,0 +1,26 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+/**
24
+ * Shows your current lag in the status bar.
25
+ */
26
+package com.dmdirc.addons.lagdisplay;

+ 39
- 0
src/com/dmdirc/addons/lagdisplay/plugin.config View File

@@ -0,0 +1,39 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  updates
9
+  requires
10
+  defaults
11
+  version
12
+
13
+metadata:
14
+  author=Chris <chris@dmdirc.com>
15
+  mainclass=com.dmdirc.addons.lagdisplay.LagDisplayPlugin
16
+  description=Displays the server lag in the status bar
17
+  name=lagdisplay
18
+  nicename=Lag Displayer
19
+
20
+requires:
21
+  parent=ui_swing
22
+
23
+updates:
24
+  id=2
25
+
26
+version:
27
+  friendly=0.6
28
+
29
+provides:
30
+  lagdisplay statusbar_entry
31
+
32
+required-services:
33
+  swing ui
34
+
35
+defaults:
36
+  usealternate=false
37
+  graph=true
38
+  labels=true
39
+  history=50

+ 100
- 0
src/com/dmdirc/addons/logging/HistoryWindow.java View File

@@ -0,0 +1,100 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.logging;
24
+
25
+import com.dmdirc.FrameContainer;
26
+import com.dmdirc.Main;
27
+import com.dmdirc.Server;
28
+import com.dmdirc.config.IdentityManager;
29
+import com.dmdirc.ui.WindowManager;
30
+import com.dmdirc.ui.interfaces.Window;
31
+
32
+/**
33
+ * Displays an extended history of a window.
34
+ *
35
+ * @author Chris
36
+ */
37
+public class HistoryWindow extends FrameContainer {
38
+
39
+    /** The window we're using. */
40
+    private Window window;
41
+
42
+    /** Our parent window. */
43
+    private Window parent;
44
+
45
+    /**
46
+     * Creates a new HistoryWindow.
47
+     *
48
+     * @param title The title of the window
49
+     * @param reader The reader to use to get the history
50
+     * @param parent The window this history window was opened from
51
+     * @param numLines The number of lines to show
52
+     */
53
+    public HistoryWindow(final String title, final ReverseFileReader reader,
54
+                         final Window parent, final int numLines) {
55
+        super("raw", title, parent.getConfigManager());
56
+
57
+        this.parent = parent;
58
+
59
+        window = Main.getUI().getWindow(this);
60
+
61
+        WindowManager.addWindow(parent, window);
62
+        window.setTitle(title);
63
+        window.open();
64
+        final int frameBufferSize = IdentityManager.getGlobalConfig().getOptionInt(
65
+                "ui", "frameBufferSize");
66
+        window.addLine(reader.getLinesAsString(Math.min(frameBufferSize, numLines)), false);
67
+    }
68
+
69
+    /** {@inheritDoc} */
70
+    @Override
71
+    public Window getFrame() {
72
+        return window;
73
+    }
74
+
75
+    /** {@inheritDoc} */
76
+    @Override
77
+    public void windowClosing() {
78
+        // 1: Make the window non-visible
79
+        window.setVisible(false);
80
+
81
+        // 2: Remove any callbacks or listeners
82
+        // 3: Trigger any actions neccessary
83
+        // 4: Trigger action for the window closing
84
+        // 5: Inform any parents that the window is closing
85
+
86
+        // 6: Remove the window from the window manager
87
+        WindowManager.removeWindow(window);
88
+
89
+        // 7: Remove any references to the window and parents
90
+        window = null; // NOPMD
91
+        parent = null; // NOPMD
92
+    }
93
+
94
+    /** {@inheritDoc} */
95
+    @Override
96
+    public Server getServer() {
97
+        return parent == null ? null : parent.getContainer().getServer();
98
+    }
99
+
100
+}

+ 144
- 0
src/com/dmdirc/addons/logging/LoggingCommand.java View File

@@ -0,0 +1,144 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.logging;
24
+
25
+import com.dmdirc.Server;
26
+import com.dmdirc.commandparser.CommandArguments;
27
+import com.dmdirc.commandparser.CommandManager;
28
+import com.dmdirc.commandparser.commands.IntelligentCommand;
29
+import com.dmdirc.commandparser.commands.ServerCommand;
30
+import com.dmdirc.plugins.Plugin;
31
+import com.dmdirc.plugins.PluginInfo;
32
+import com.dmdirc.plugins.PluginManager;
33
+import com.dmdirc.ui.input.AdditionalTabTargets;
34
+import com.dmdirc.ui.interfaces.InputWindow;
35
+import java.util.List;
36
+
37
+/**
38
+ * The dcop command retrieves information from a dcop application.
39
+ *
40
+ * @author Shane "Dataforce" Mc Cormack
41
+ */
42
+public final class LoggingCommand extends ServerCommand implements IntelligentCommand {
43
+
44
+    /**
45
+     * Creates a new instance of LoggingCommand.
46
+     */
47
+    public LoggingCommand() {
48
+        super();
49
+        CommandManager.registerCommand(this);
50
+    }
51
+
52
+    /** {@inheritDoc} */
53
+    @Override
54
+    public void execute(final InputWindow origin, final Server server,
55
+                        final boolean isSilent, final CommandArguments args) {
56
+        final PluginInfo pluginInfo = PluginManager.getPluginManager().getPluginInfoByName("logging");
57
+        if (pluginInfo == null) {
58
+            sendLine(origin, isSilent, FORMAT_ERROR, "Logging Plugin is not loaded.");
59
+            return;
60
+        }
61
+        final Plugin gotPlugin = pluginInfo.getPlugin();
62
+
63
+        if (!(gotPlugin instanceof LoggingPlugin)) {
64
+            sendLine(origin, isSilent, FORMAT_ERROR, "Logging Plugin is not loaded.");
65
+            return;
66
+        }
67
+
68
+        final LoggingPlugin plugin = (LoggingPlugin) gotPlugin;
69
+
70
+        if (args.getArguments().length > 0) {
71
+            if (args.getArguments()[0].equalsIgnoreCase("reload")) {
72
+                if (PluginManager.getPluginManager().reloadPlugin(pluginInfo.getFilename())) {
73
+                    sendLine(origin, isSilent, FORMAT_OUTPUT, "Plugin reloaded.");
74
+                } else {
75
+                    sendLine(origin, isSilent, FORMAT_ERROR, "Plugin failed to reload.");
76
+                }
77
+            } else if (args.getArguments()[0].equalsIgnoreCase("history")) {
78
+                if (!plugin.showHistory(origin)) {
79
+                    sendLine(origin, isSilent, FORMAT_ERROR, "Unable to open history for this window.");
80
+                }
81
+            } else if (args.getArguments()[0].equalsIgnoreCase("help")) {
82
+                sendLine(origin, isSilent, FORMAT_OUTPUT, getName() + " reload           - Reload the logging plugin.");
83
+                sendLine(origin, isSilent, FORMAT_OUTPUT, getName() + " history          - Open the history of this window, if available.");
84
+                sendLine(origin, isSilent, FORMAT_OUTPUT, getName() + " help             - Show this help.");
85
+            } else {
86
+                sendLine(origin, isSilent, FORMAT_ERROR, "Unknown command '" + args.getArguments()[0] + "'. Use " + getName() + " help for a list of commands.");
87
+            }
88
+        } else {
89
+            sendLine(origin, isSilent, FORMAT_ERROR, "Use " + getName() + " help for a list of commands.");
90
+        }
91
+    }
92
+
93
+    /**
94
+     * Returns a list of suggestions for the specified argument, given the list
95
+     * of previous arguments.
96
+     *
97
+     * @param arg The argument that is being completed
98
+     * @param previousArgs The contents of the previous arguments, if any
99
+     * @return A list of suggestions for the argument
100
+     */
101
+    @Override
102
+    public AdditionalTabTargets getSuggestions(final int arg, final List<String> previousArgs) {
103
+        final AdditionalTabTargets res = new AdditionalTabTargets();
104
+        if (arg == 0) {
105
+            res.add("reload");
106
+            res.add("history");
107
+            res.add("help");
108
+            res.excludeAll();
109
+        }
110
+        return res;
111
+    }
112
+
113
+    /**
114
+     * Returns this command's name.
115
+     *
116
+     * @return The name of this command
117
+     */
118
+    @Override
119
+    public String getName() {
120
+        return "logging";
121
+    }
122
+
123
+    /**
124
+     * Returns whether or not this command should be shown in help messages.
125
+     *
126
+     * @return True iff the command should be shown, false otherwise
127
+     */
128
+    @Override
129
+    public boolean showInHelp() {
130
+        return true;
131
+    }
132
+
133
+    /**
134
+     * Returns a string representing the help message for this command.
135
+     *
136
+     * @return the help message for this command
137
+     */
138
+    @Override
139
+    public String getHelp() {
140
+        return this.getName() + " <set|help> [parameters]";
141
+    }
142
+
143
+}
144
+

+ 821
- 0
src/com/dmdirc/addons/logging/LoggingPlugin.java View File

@@ -0,0 +1,821 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.logging;
24
+
25
+import com.dmdirc.Channel;
26
+import com.dmdirc.Main;
27
+import com.dmdirc.Query;
28
+import com.dmdirc.Server;
29
+import com.dmdirc.actions.ActionManager;
30
+import com.dmdirc.actions.CoreActionType;
31
+import com.dmdirc.actions.interfaces.ActionType;
32
+import com.dmdirc.commandparser.CommandManager;
33
+import com.dmdirc.config.IdentityManager;
34
+import com.dmdirc.config.prefs.PreferencesCategory;
35
+import com.dmdirc.config.prefs.PreferencesManager;
36
+import com.dmdirc.config.prefs.PreferencesSetting;
37
+import com.dmdirc.config.prefs.PreferencesType;
38
+import com.dmdirc.interfaces.ActionListener;
39
+import com.dmdirc.logger.ErrorLevel;
40
+import com.dmdirc.logger.Logger;
41
+import com.dmdirc.parser.interfaces.ChannelClientInfo;
42
+import com.dmdirc.parser.interfaces.ChannelInfo;
43
+import com.dmdirc.parser.interfaces.ClientInfo;
44
+import com.dmdirc.parser.interfaces.Parser;
45
+import com.dmdirc.plugins.Plugin;
46
+import com.dmdirc.ui.interfaces.InputWindow;
47
+import com.dmdirc.ui.interfaces.Window;
48
+import com.dmdirc.ui.messages.Styliser;
49
+
50
+import java.awt.Color;
51
+import java.io.BufferedWriter;
52
+import java.io.File;
53
+import java.io.FileNotFoundException;
54
+import java.io.FileWriter;
55
+import java.io.IOException;
56
+import java.math.BigInteger;
57
+import java.security.MessageDigest;
58
+import java.security.NoSuchAlgorithmException;
59
+import java.text.DateFormat;
60
+import java.text.SimpleDateFormat;
61
+import java.util.Date;
62
+import java.util.Hashtable;
63
+import java.util.Map;
64
+import java.util.Stack;
65
+
66
+import java.util.Timer;
67
+import java.util.TimerTask;
68
+
69
+/**
70
+ * Adds logging facility to client.
71
+ *
72
+ * @author Shane 'Dataforce' McCormack
73
+ */
74
+public class LoggingPlugin extends Plugin implements ActionListener {
75
+
76
+    /** The command we registered. */
77
+    private LoggingCommand command;
78
+
79
+    /** Open File */
80
+    protected class OpenFile {
81
+
82
+        public long lastUsedTime = System.currentTimeMillis();
83
+
84
+        public BufferedWriter writer = null;
85
+
86
+        public OpenFile(final BufferedWriter writer) {
87
+            this.writer = writer;
88
+        }
89
+
90
+    }
91
+
92
+    /** Timer used to close idle files */
93
+    protected Timer idleFileTimer;
94
+
95
+    /** Hashtable of open files. */
96
+    protected final Map<String, OpenFile> openFiles = new Hashtable<String, OpenFile>();
97
+
98
+    /** Date format used for "File Opened At" log. */
99
+    final DateFormat openedAtFormat = new SimpleDateFormat("EEEE MMMM dd, yyyy - HH:mm:ss");
100
+
101
+    /**
102
+     * Creates a new instance of the Logging Plugin.
103
+     */
104
+    public LoggingPlugin() {
105
+        super();
106
+    }
107
+
108
+    /** {@inheritDoc} */
109
+    @Override
110
+    public void domainUpdated() {
111
+        IdentityManager.getAddonIdentity().setOption(getDomain(),
112
+                "general.directory", Main.getConfigDir() + "logs" + System.getProperty("file.separator"));
113
+    }
114
+
115
+    /**
116
+     * Called when the plugin is loaded.
117
+     */
118
+    @Override
119
+    public void onLoad() {
120
+        final File dir = new File(IdentityManager.getGlobalConfig().getOption(getDomain(), "general.directory"));
121
+        if (dir.exists()) {
122
+            if (!dir.isDirectory()) {
123
+                Logger.userError(ErrorLevel.LOW, "Unable to create logging dir (file exists instead)");
124
+            }
125
+        } else {
126
+            if (!dir.mkdirs()) {
127
+                Logger.userError(ErrorLevel.LOW, "Unable to create logging dir");
128
+            }
129
+        }
130
+
131
+        command = new LoggingCommand();
132
+        ActionManager.addListener(this,
133
+                CoreActionType.CHANNEL_OPENED,
134
+                CoreActionType.CHANNEL_CLOSED,
135
+                CoreActionType.CHANNEL_MESSAGE,
136
+                CoreActionType.CHANNEL_SELF_MESSAGE,
137
+                CoreActionType.CHANNEL_ACTION,
138
+                CoreActionType.CHANNEL_SELF_ACTION,
139
+                CoreActionType.CHANNEL_GOTTOPIC,
140
+                CoreActionType.CHANNEL_TOPICCHANGE,
141
+                CoreActionType.CHANNEL_JOIN,
142
+                CoreActionType.CHANNEL_PART,
143
+                CoreActionType.CHANNEL_QUIT,
144
+                CoreActionType.CHANNEL_KICK,
145
+                CoreActionType.CHANNEL_NICKCHANGE,
146
+                CoreActionType.CHANNEL_MODECHANGE,
147
+                CoreActionType.QUERY_OPENED,
148
+                CoreActionType.QUERY_CLOSED,
149
+                CoreActionType.QUERY_MESSAGE,
150
+                CoreActionType.QUERY_SELF_MESSAGE,
151
+                CoreActionType.QUERY_ACTION,
152
+                CoreActionType.QUERY_SELF_ACTION);
153
+
154
+        // Close idle files every hour.
155
+        idleFileTimer = new Timer("LoggingPlugin Timer");
156
+        idleFileTimer.schedule(new TimerTask() {
157
+
158
+            /** {@inheritDoc} */
159
+            @Override
160
+            public void run() {
161
+                timerTask();
162
+            }
163
+
164
+        }, 3600000);
165
+    }
166
+
167
+    /**
168
+     * What to do every hour when the timer fires.
169
+     */
170
+    protected void timerTask() {
171
+        // Oldest time to allow
172
+        final long oldestTime = System.currentTimeMillis() - 3480000;
173
+
174
+        synchronized (openFiles) {
175
+            for (String filename : (new Hashtable<String, OpenFile>(openFiles)).keySet()) {
176
+                OpenFile file = openFiles.get(filename);
177
+                if (file.lastUsedTime < oldestTime) {
178
+                    try {
179
+                        file.writer.close();
180
+                        openFiles.remove(filename);
181
+                    } catch (IOException e) {
182
+                        Logger.userError(ErrorLevel.LOW, "Unable to close idle file (File: " + filename + ")");
183
+                    }
184
+                }
185
+            }
186
+        }
187
+    }
188
+
189
+    /**
190
+     * Called when this plugin is unloaded.
191
+     */
192
+    @Override
193
+    public void onUnload() {
194
+        idleFileTimer.cancel();
195
+        idleFileTimer.purge();
196
+
197
+        CommandManager.unregisterCommand(command);
198
+        ActionManager.removeListener(this);
199
+
200
+        synchronized (openFiles) {
201
+            for (String filename : openFiles.keySet()) {
202
+                OpenFile file = openFiles.get(filename);
203
+                try {
204
+                    file.writer.close();
205
+                } catch (IOException e) {
206
+                    Logger.userError(ErrorLevel.LOW, "Unable to close file (File: " + filename + ")");
207
+                }
208
+            }
209
+            openFiles.clear();
210
+        }
211
+    }
212
+
213
+    /** {@inheritDoc} */
214
+    @Override
215
+    public void showConfig(final PreferencesManager manager) {
216
+        final PreferencesCategory general = new PreferencesCategory("Logging", "General configuration for Logging plugin.");
217
+        final PreferencesCategory backbuffer = new PreferencesCategory("Back Buffer", "Options related to the automatic backbuffer");
218
+        final PreferencesCategory advanced = new PreferencesCategory("Advanced", "Advanced configuration for Logging plugin. You shouldn't need to edit this unless you know what you are doing.");
219
+
220
+        general.addSetting(new PreferencesSetting(PreferencesType.TEXT, getDomain(), "general.directory", "Directory", "Directory for log files"));
221
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "general.networkfolders", "Separate logs by network", "Should the files be stored in a sub-dir with the networks name?"));
222
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "general.addtime", "Timestamp logs", "Should a timestamp be added to the log files?"));
223
+        general.addSetting(new PreferencesSetting(PreferencesType.TEXT, getDomain(), "general.timestamp", "Timestamp format", "The String to pass to 'SimpleDateFormat' to format the timestamp"));
224
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "general.stripcodes", "Strip Control Codes", "Remove known irc control codes from lines before saving?"));
225
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "general.channelmodeprefix", "Show channel mode prefix", "Show the @,+ etc next to nicknames"));
226
+
227
+        backbuffer.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "backbuffer.autobackbuffer", "Automatically display", "Automatically display the backbuffer when a channel is joined"));
228
+        backbuffer.addSetting(new PreferencesSetting(PreferencesType.COLOUR, getDomain(), "backbuffer.colour", "Colour to use for display", "Colour used when displaying the backbuffer"));
229
+        backbuffer.addSetting(new PreferencesSetting(PreferencesType.INTEGER, getDomain(), "backbuffer.lines", "Number of lines to show", "Number of lines used when displaying backbuffer"));
230
+        backbuffer.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "backbuffer.timestamp", "Show Formatter-Timestamp", "Should the line be added to the frame with the timestamp from the formatter aswell as the file contents"));
231
+
232
+        advanced.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "advanced.filenamehash", "Add Filename hash", "Add the MD5 hash of the channel/client name to the filename. (This is used to allow channels with similar names (ie a _ not a  -) to be logged separately)"));
233
+
234
+        advanced.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN, getDomain(), "advanced.usedate", "Use Date directories", "Should the log files be in separate directories based on the date?"));
235
+        advanced.addSetting(new PreferencesSetting(PreferencesType.TEXT, getDomain(), "advanced.usedateformat", "Archive format", "The String to pass to 'SimpleDateFormat' to format the directory name(s) for archiving"));
236
+
237
+        general.addSubCategory(backbuffer.setInline());
238
+        general.addSubCategory(advanced.setInline());
239
+        manager.getCategory("Plugins").addSubCategory(general.setInlineAfter());
240
+    }
241
+
242
+    /**
243
+     * Log a query-related event
244
+     *
245
+     * @param type The type of the event to process
246
+     * @param format Format of messages that are about to be sent. (May be null)
247
+     * @param arguments The arguments for the event
248
+     */
249
+    protected void handleQueryEvent(final CoreActionType type, final StringBuffer format, final Object... arguments) {
250
+        final Query query = (Query) arguments[0];
251
+        if (query.getServer() == null) {
252
+            Logger.appError(ErrorLevel.MEDIUM, "Query object has no server (" + type.toString() + ")", new Exception("Query object has no server (" + type.toString() + ")"));
253
+            return;
254
+        }
255
+
256
+        final Parser parser = query.getServer().getParser();
257
+        ClientInfo client;
258
+
259
+        if (parser == null) {
260
+            // Without a parser object, we might not be able to find the file to log this to.
261
+            if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "general.networkfolders")) {
262
+                // We *wont* be able to, so rather than logging to an incorrect file we just won't log.
263
+                return;
264
+            }
265
+            client = null;
266
+        } else {
267
+            client = parser.getClient(query.getHost());
268
+        }
269
+
270
+        final String filename = getLogFile(client);
271
+
272
+        switch (type) {
273
+            case QUERY_OPENED:
274
+                if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "backbuffer.autobackbuffer")) {
275
+                    showBackBuffer(query.getFrame(), filename);
276
+                }
277
+
278
+                appendLine(filename, "*** Query opened at: %s", openedAtFormat.format(new Date()));
279
+                appendLine(filename, "*** Query with User: %s", query.getHost());
280
+                appendLine(filename, "");
281
+                break;
282
+            case QUERY_CLOSED:
283
+                appendLine(filename, "*** Query closed at: %s", openedAtFormat.format(new Date()));
284
+                if (openFiles.containsKey(filename)) {
285
+                    final BufferedWriter file = openFiles.get(filename).writer;
286
+                    try {
287
+                        file.close();
288
+                    } catch (IOException e) {
289
+                        Logger.userError(ErrorLevel.LOW, "Unable to close file (Filename: " + filename + ")");
290
+                    }
291
+                    openFiles.remove(filename);
292
+                }
293
+                break;
294
+            case QUERY_MESSAGE:
295
+            case QUERY_SELF_MESSAGE:
296
+            case QUERY_ACTION:
297
+            case QUERY_SELF_ACTION:
298
+                final boolean isME = (type == CoreActionType.QUERY_SELF_MESSAGE || type == CoreActionType.QUERY_SELF_ACTION);
299
+                final String overrideNick = (isME) ? getDisplayName(parser.getLocalClient()) : "";
300
+
301
+                if (type == CoreActionType.QUERY_MESSAGE || type == CoreActionType.QUERY_SELF_MESSAGE) {
302
+                    appendLine(filename, "<%s> %s", getDisplayName(client, overrideNick), (String) arguments[1]);
303
+                } else {
304
+                    appendLine(filename, "* %s %s", getDisplayName(client, overrideNick), (String) arguments[1]);
305
+                }
306
+                break;
307
+        }
308
+    }
309
+
310
+    /**
311
+     * Log a channel-related event
312
+     *
313
+     * @param type The type of the event to process
314
+     * @param format Format of messages that are about to be sent. (May be null)
315
+     * @param arguments The arguments for the event
316
+     */
317
+    protected void handleChannelEvent(final CoreActionType type, final StringBuffer format, final Object... arguments) {
318
+        final Channel chan = ((Channel) arguments[0]);
319
+        final ChannelInfo channel = chan.getChannelInfo();
320
+        final String filename = getLogFile(channel);
321
+
322
+        final ChannelClientInfo channelClient = (arguments.length > 1 && arguments[1] instanceof ChannelClientInfo) ? (ChannelClientInfo) arguments[1] : null;
323
+        final ClientInfo client = (channelClient != null) ? channelClient.getClient() : null;
324
+
325
+        final String message = (arguments.length > 2 && arguments[2] instanceof String) ? (String) arguments[2] : null;
326
+
327
+        switch (type) {
328
+            case CHANNEL_OPENED:
329
+                if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "backbuffer.autobackbuffer")) {
330
+                    showBackBuffer(chan.getFrame(), filename);
331
+                }
332
+
333
+                appendLine(filename, "*** Channel opened at: %s", openedAtFormat.format(new Date()));
334
+                appendLine(filename, "");
335
+                break;
336
+            case CHANNEL_CLOSED:
337
+                appendLine(filename, "*** Channel closed at: %s", openedAtFormat.format(new Date()));
338
+                if (openFiles.containsKey(filename)) {
339
+                    final BufferedWriter file = openFiles.get(filename).writer;
340
+                    try {
341
+                        file.close();
342
+                    } catch (IOException e) {
343
+                        Logger.userError(ErrorLevel.LOW, "Unable to close file (Filename: " + filename + ")");
344
+                    }
345
+                    openFiles.remove(filename);
346
+                }
347
+                break;
348
+            case CHANNEL_MESSAGE:
349
+            case CHANNEL_SELF_MESSAGE:
350
+            case CHANNEL_ACTION:
351
+            case CHANNEL_SELF_ACTION:
352
+                if (type == CoreActionType.CHANNEL_MESSAGE || type == CoreActionType.CHANNEL_SELF_MESSAGE) {
353
+                    appendLine(filename, "<%s> %s", getDisplayName(client), message);
354
+                } else {
355
+                    appendLine(filename, "* %s %s", getDisplayName(client), message);
356
+                }
357
+                break;
358
+            case CHANNEL_GOTTOPIC:
359
+                // ActionManager.processEvent(CoreActionType.CHANNEL_GOTTOPIC, this);
360
+                final DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
361
+                final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
362
+
363
+                appendLine(filename, "*** Topic is: %s", channel.getTopic());
364
+                appendLine(filename, "*** Set at: %s on %s by %s", timeFormat.format(1000 * channel.getTopicTime()), dateFormat.format(1000 * channel.getTopicTime()), channel.getTopicSetter());
365
+                break;
366
+            case CHANNEL_TOPICCHANGE:
367
+                appendLine(filename, "*** %s Changed the topic to: %s", getDisplayName(channelClient), message);
368
+                break;
369
+            case CHANNEL_JOIN:
370
+                appendLine(filename, "*** %s (%s) joined the channel", getDisplayName(channelClient), client.toString());
371
+                break;
372
+            case CHANNEL_PART:
373
+                if (message.isEmpty()) {
374
+                    appendLine(filename, "*** %s (%s) left the channel", getDisplayName(channelClient), client.toString());
375
+                } else {
376
+                    appendLine(filename, "*** %s (%s) left the channel (%s)", getDisplayName(channelClient), client.toString(), message);
377
+                }
378
+                break;
379
+            case CHANNEL_QUIT:
380
+                if (message.isEmpty()) {
381
+                    appendLine(filename, "*** %s (%s) Quit IRC", getDisplayName(channelClient), client.toString());
382
+                } else {
383
+                    appendLine(filename, "*** %s (%s) Quit IRC (%s)", getDisplayName(channelClient), client.toString(), message);
384
+                }
385
+                break;
386
+            case CHANNEL_KICK:
387
+                final String kickReason = (String) arguments[3];
388
+                final ChannelClientInfo kickedClient = (ChannelClientInfo) arguments[2];
389
+
390
+                if (kickReason.isEmpty()) {
391
+                    appendLine(filename, "*** %s was kicked by %s", getDisplayName(kickedClient), getDisplayName(channelClient));
392
+                } else {
393
+                    appendLine(filename, "*** %s was kicked by %s (%s)", getDisplayName(kickedClient), getDisplayName(channelClient), kickReason);
394
+                }
395
+                break;
396
+            case CHANNEL_NICKCHANGE:
397
+                appendLine(filename, "*** %s is now %s", getDisplayName(channelClient, message), getDisplayName(channelClient));
398
+                break;
399
+            case CHANNEL_MODECHANGE:
400
+                if (channelClient.getClient().getNickname().isEmpty()) {
401
+                    appendLine(filename, "*** Channel modes are: %s", message);
402
+                } else {
403
+                    appendLine(filename, "*** %s set modes: %s", getDisplayName(channelClient), message);
404
+                }
405
+                break;
406
+        }
407
+    }
408
+
409
+    /**
410
+     * Process an event of the specified type.
411
+     *
412
+     * @param type The type of the event to process
413
+     * @param format Format of messages that are about to be sent. (May be null)
414
+     * @param arguments The arguments for the event
415
+     */
416
+    @Override
417
+    public void processEvent(final ActionType type, final StringBuffer format, final Object... arguments) {
418
+        if (type instanceof CoreActionType) {
419
+            final CoreActionType thisType = (CoreActionType) type;
420
+
421
+            switch (thisType) {
422
+                case CHANNEL_OPENED:
423
+                case CHANNEL_CLOSED:
424
+                case CHANNEL_MESSAGE:
425
+                case CHANNEL_SELF_MESSAGE:
426
+                case CHANNEL_ACTION:
427
+                case CHANNEL_SELF_ACTION:
428
+                case CHANNEL_GOTTOPIC:
429
+                case CHANNEL_TOPICCHANGE:
430
+                case CHANNEL_JOIN:
431
+                case CHANNEL_PART:
432
+                case CHANNEL_QUIT:
433
+                case CHANNEL_KICK:
434
+                case CHANNEL_NICKCHANGE:
435
+                case CHANNEL_MODECHANGE:
436
+                    handleChannelEvent(thisType, format, arguments);
437
+                    break;
438
+                case QUERY_OPENED:
439
+                case QUERY_CLOSED:
440
+                case QUERY_MESSAGE:
441
+                case QUERY_SELF_MESSAGE:
442
+                case QUERY_ACTION:
443
+                case QUERY_SELF_ACTION:
444
+                    handleQueryEvent(thisType, format, arguments);
445
+                    break;
446
+                default:
447
+                    break;
448
+            }
449
+        }
450
+    }
451
+
452
+    /**
453
+     * Add a backbuffer to a frame.
454
+     *
455
+     * @param frame The frame to add the backbuffer lines to
456
+     * @param filename File to get backbuffer from
457
+     */
458
+    protected void showBackBuffer(final Window frame, final String filename) {
459
+        final int numLines = IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "backbuffer.lines");
460
+        final String colour = IdentityManager.getGlobalConfig().getOption(getDomain(), "backbuffer.colour");
461
+        final boolean showTimestamp = IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "backbuffer.timestamp");
462
+        if (frame == null) {
463
+            Logger.userError(ErrorLevel.LOW, "Given a null frame");
464
+            return;
465
+        }
466
+
467
+        final File testFile = new File(filename);
468
+        if (testFile.exists()) {
469
+            try {
470
+                final ReverseFileReader file = new ReverseFileReader(testFile);
471
+                // Because the file includes a newline char at the end, an empty line
472
+                // is returned by getLines. To counter this, we call getLines(1) and do
473
+                // nothing with the output.
474
+                file.getLines(1);
475
+                final Stack<String> lines = file.getLines(numLines);
476
+                while (!lines.empty()) {
477
+                    frame.addLine(getColouredString(colour, lines.pop()), showTimestamp);
478
+                }
479
+                file.close();
480
+                frame.addLine(getColouredString(colour, "--- End of backbuffer\n"), showTimestamp);
481
+            } catch (FileNotFoundException e) {
482
+                Logger.userError(ErrorLevel.LOW, "Unable to show backbuffer (Filename: " + filename + "): " + e.getMessage());
483
+            } catch (IOException e) {
484
+                Logger.userError(ErrorLevel.LOW, "Unable to show backbuffer (Filename: " + filename + "): " + e.getMessage());
485
+            } catch (SecurityException e) {
486
+                Logger.userError(ErrorLevel.LOW, "Unable to show backbuffer (Filename: " + filename + "): " + e.getMessage());
487
+            }
488
+        }
489
+    }
490
+
491
+    /**
492
+     * Get a coloured String.
493
+     * If colour is invalid, IRC Colour 14 will be used.
494
+     *
495
+     * @param colour The colour the string should be (IRC Colour or 6-digit hex colour)
496
+     * @param line the line to colour
497
+     * @return The given line with the appropriate irc codes appended/prepended to colour it.
498
+     */
499
+    protected static String getColouredString(final String colour, final String line) {
500
+        String res = null;
501
+        if (colour.length() < 3) {
502
+            int num;
503
+
504
+            try {
505
+                num = Integer.parseInt(colour);
506
+            } catch (NumberFormatException ex) {
507
+                num = -1;
508
+            }
509
+
510
+            if (num >= 0 && num <= 15) {
511
+                res = String.format("%c%02d%s%1$c", Styliser.CODE_COLOUR, num, line);
512
+            }
513
+        } else if (colour.length() == 6) {
514
+            try {
515
+                Color.decode("#" + colour);
516
+                res = String.format("%c%s%s%1$c", Styliser.CODE_HEXCOLOUR, colour, line);
517
+            } catch (NumberFormatException ex) { /* Do Nothing */ }
518
+        }
519
+
520
+        if (res == null) {
521
+            res = String.format("%c%02d%s%1$c", Styliser.CODE_COLOUR, 14, line);
522
+        }
523
+        return res;
524
+    }
525
+
526
+    /**
527
+     * Add a line to a file.
528
+     *
529
+     * @param filename Name of file to write to
530
+     * @param format Format of line to add. (NewLine will be added Automatically)
531
+     * @param args Arguments for format
532
+     * @return true on success, else false.
533
+     */
534
+    protected boolean appendLine(final String filename, final String format, final Object... args) {
535
+        return appendLine(filename, String.format(format, args));
536
+    }
537
+
538
+    /**
539
+     * Add a line to a file.
540
+     *
541
+     * @param filename Name of file to write to
542
+     * @param line Line to add. (NewLine will be added Automatically)
543
+     * @return true on success, else false.
544
+     */
545
+    protected boolean appendLine(final String filename, final String line) {
546
+        final StringBuffer finalLine = new StringBuffer();
547
+
548
+        if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "general.addtime")) {
549
+            String dateString;
550
+            final String dateFormatString = IdentityManager.getGlobalConfig().getOption(getDomain(), "general.timestamp");
551
+            try {
552
+                final DateFormat dateFormat = new SimpleDateFormat(dateFormatString);
553
+                dateString = dateFormat.format(new Date()).trim();
554
+            } catch (IllegalArgumentException iae) {
555
+                // Default to known good format
556
+                final DateFormat dateFormat = new SimpleDateFormat("[dd/MM/yyyy HH:mm:ss]");
557
+                dateString = dateFormat.format(new Date()).trim();
558
+
559
+                Logger.userError(ErrorLevel.LOW, "Dateformat String '" + dateFormatString + "' is invalid. For more information: http://java.sun.com/javase/6/docs/api/java/text/SimpleDateFormat.html");
560
+            }
561
+            finalLine.append(dateString);
562
+            finalLine.append(" ");
563
+        }
564
+
565
+        if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "general.stripcodes")) {
566
+            finalLine.append(Styliser.stipControlCodes(line));
567
+        } else {
568
+            finalLine.append(line);
569
+        }
570
+        //System.out.println("[Adding] "+filename+" => "+finalLine);
571
+        BufferedWriter out = null;
572
+        try {
573
+            if (openFiles.containsKey(filename)) {
574
+                OpenFile of = openFiles.get(filename);
575
+                of.lastUsedTime = System.currentTimeMillis();
576
+                out = of.writer;
577
+            } else {
578
+                out = new BufferedWriter(new FileWriter(filename, true));
579
+                openFiles.put(filename, new OpenFile(out));
580
+            }
581
+            out.write(finalLine.toString());
582
+            out.newLine();
583
+            out.flush();
584
+            return true;
585
+        } catch (IOException e) {
586
+            /*
587
+             * Do Nothing
588
+             *
589
+             * Makes no sense to keep adding errors to the logger when we can't
590
+             * write to the file, as chances are it will happen on every incomming
591
+             * line.
592
+             */
593
+        }
594
+        return false;
595
+    }
596
+
597
+    /**
598
+     * Get the name of the log file for a specific object.
599
+     *
600
+     * @param obj Object to get name for
601
+     * @return the name of the log file to use for this object.
602
+     */
603
+    protected String getLogFile(final Object obj) {
604
+        final StringBuffer directory = new StringBuffer();
605
+        final StringBuffer file = new StringBuffer();
606
+        String md5String = "";
607
+
608
+        directory.append(IdentityManager.getGlobalConfig().getOption(getDomain(), "general.directory"));
609
+        if (directory.charAt(directory.length() - 1) != File.separatorChar) {
610
+            directory.append(File.separatorChar);
611
+        }
612
+
613
+        if (obj == null) {
614
+            file.append("null.log");
615
+        } else if (obj instanceof ChannelInfo) {
616
+            final ChannelInfo channel = (ChannelInfo) obj;
617
+            if (channel.getParser() != null) {
618
+                addNetworkDir(directory, file, channel.getParser().getNetworkName());
619
+            }
620
+            file.append(sanitise(channel.getName().toLowerCase()));
621
+            md5String = channel.getName();
622
+        } else if (obj instanceof ClientInfo) {
623
+            final ClientInfo client = (ClientInfo) obj;
624
+            if (client.getParser() != null) {
625
+                addNetworkDir(directory, file, client.getParser().getNetworkName());
626
+            }
627
+            file.append(sanitise(client.getNickname().toLowerCase()));
628
+            md5String = client.getNickname();
629
+        } else {
630
+            file.append(sanitise(obj.toString().toLowerCase()));
631
+            md5String = obj.toString();
632
+        }
633
+
634
+        if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "advanced.usedate")) {
635
+            final String dateFormat = IdentityManager.getGlobalConfig().getOption(getDomain(), "advanced.usedateformat");
636
+            final String dateDir = (new SimpleDateFormat(dateFormat)).format(new Date());
637
+            directory.append(dateDir);
638
+            if (directory.charAt(directory.length() - 1) != File.separatorChar) {
639
+                directory.append(File.separatorChar);
640
+            }
641
+
642
+            if (!new File(directory.toString()).exists() && !(new File(directory.toString())).mkdirs()) {
643
+                Logger.userError(ErrorLevel.LOW, "Unable to create date dirs");
644
+            }
645
+        }
646
+
647
+        if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "advanced.filenamehash")) {
648
+            file.append('.');
649
+            file.append(md5(md5String));
650
+        }
651
+        file.append(".log");
652
+
653
+        return directory.toString() + file.toString();
654
+    }
655
+
656
+    /**
657
+     * This function adds the networkName to the log file.
658
+     * It first tries to create a directory for each network, if that fails
659
+     * it will prepend the networkName to the filename instead.
660
+     *
661
+     * @param directory Current directory name
662
+     * @param file Current file name
663
+     * @param networkName Name of network
664
+     */
665
+    protected void addNetworkDir(final StringBuffer directory, final StringBuffer file, final String networkName) {
666
+        if (!IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "general.networkfolders")) {
667
+            return;
668
+        }
669
+
670
+        final String network = sanitise(networkName.toLowerCase());
671
+
672
+        boolean prependNetwork = false;
673
+
674
+        // Check dir exists
675
+        final File dir = new File(directory.toString() + network + System.getProperty("file.separator"));
676
+        if (dir.exists() && !dir.isDirectory()) {
677
+            Logger.userError(ErrorLevel.LOW, "Unable to create networkfolders dir (file exists instead)");
678
+            // Prepend network name to file instead.
679
+            prependNetwork = true;
680
+        } else if (!dir.exists() && !dir.mkdirs()) {
681
+            Logger.userError(ErrorLevel.LOW, "Unable to create networkfolders dir");
682
+            prependNetwork = true;
683
+        }
684
+
685
+        if (prependNetwork) {
686
+            file.insert(0, " -- ");
687
+            file.insert(0, network);
688
+        } else {
689
+            directory.append(network);
690
+            directory.append(System.getProperty("file.separator"));
691
+        }
692
+    }
693
+
694
+    /**
695
+     * Sanitise a string to be used as a filename.
696
+     *
697
+     * @param name String to sanitise
698
+     * @return Sanitised version of name that can be used as a filename.
699
+     */
700
+    protected static String sanitise(final String name) {
701
+        // Replace illegal chars with
702
+        return name.replaceAll("[^\\w\\.\\s\\-\\#\\&\\_]", "_");
703
+    }
704
+
705
+    /**
706
+     * Get the md5 hash of a string.
707
+     *
708
+     * @param string String to hash
709
+     * @return md5 hash of given string
710
+     */
711
+    protected static String md5(final String string) {
712
+        try {
713
+            final MessageDigest m = MessageDigest.getInstance("MD5");
714
+            m.update(string.getBytes(), 0, string.length());
715
+            return new BigInteger(1, m.digest()).toString(16);
716
+        } catch (NoSuchAlgorithmException e) {
717
+            return "";
718
+        }
719
+    }
720
+
721
+    /**
722
+     * Get name to display for client.
723
+     *
724
+     * @param client The client to get the display name for
725
+     * @return name to display
726
+     */
727
+    protected String getDisplayName(final ClientInfo client) {
728
+        return getDisplayName(client, "");
729
+    }
730
+
731
+    /**
732
+     * Get name to display for client.
733
+     *
734
+     * @param client The client to get the display name for
735
+     * @param overrideNick Nickname to display instead of real nickname
736
+     * @return name to display
737
+     */
738
+    protected String getDisplayName(final ClientInfo client, final String overrideNick) {
739
+        if (overrideNick.isEmpty()) {
740
+            return (client == null) ? "Unknown Client" : client.getNickname();
741
+        } else {
742
+            return overrideNick;
743
+        }
744
+    }
745
+
746
+    /**
747
+     * Get name to display for channelClient (Taking into account the channelmodeprefix setting).
748
+     *
749
+     * @param channelClient The client to get the display name for
750
+     * @return name to display
751
+     */
752
+    protected String getDisplayName(final ChannelClientInfo channelClient) {
753
+        return getDisplayName(channelClient, "");
754
+    }
755
+
756
+    /**
757
+     * Get name to display for channelClient (Taking into account the channelmodeprefix setting).
758
+     *
759
+     * @param channelClient The client to get the display name for
760
+     * @param overrideNick Nickname to display instead of real nickname
761
+     * @return name to display
762
+     */
763
+    protected String getDisplayName(final ChannelClientInfo channelClient, final String overrideNick) {
764
+        final boolean addModePrefix = (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "general.channelmodeprefix"));
765
+
766
+        if (channelClient == null) {
767
+            return (overrideNick.isEmpty()) ? "Unknown Client" : overrideNick;
768
+        } else if (overrideNick.isEmpty()) {
769
+            return (addModePrefix) ? channelClient.toString() : channelClient.getClient().getNickname();
770
+        } else {
771
+            return (addModePrefix) ? channelClient.getImportantModePrefix() + overrideNick : overrideNick;
772
+        }
773
+    }
774
+
775
+    /**
776
+     * Shows the history window for the specified target, if available.
777
+     *
778
+     * @param target The window whose history we're trying to open
779
+     * @return True if the history is available, false otherwise
780
+     */
781
+    protected boolean showHistory(final InputWindow target) {
782
+        Object component;
783
+
784
+        if (target.getContainer() instanceof Channel) {
785
+            component = ((Channel) target.getContainer()).getChannelInfo();
786
+        } else if (target.getContainer() instanceof Query) {
787
+            final Parser parser = ((Query) target.getContainer()).getServer().getParser();
788
+            component = parser.getClient(((Query) target.getContainer()).getHost());
789
+        } else if (target.getContainer() instanceof Server) {
790
+            component = target.getContainer().getServer().getParser();
791
+        } else {
792
+            // Unknown component
793
+            return false;
794
+        }
795
+
796
+        final String log = getLogFile(component);
797
+
798
+        if (!new File(log).exists()) {
799
+            // File doesn't exist
800
+            return false;
801
+        }
802
+
803
+        ReverseFileReader reader;
804
+
805
+        try {
806
+            reader = new ReverseFileReader(log);
807
+        } catch (FileNotFoundException ex) {
808
+            return false;
809
+        } catch (IOException ex) {
810
+            return false;
811
+        } catch (SecurityException ex) {
812
+            return false;
813
+        }
814
+
815
+        new HistoryWindow("History", reader, target,
816
+                IdentityManager.getGlobalConfig().getOptionInt(getDomain(), "history.lines"));
817
+
818
+        return true;
819
+    }
820
+
821
+}

+ 261
- 0
src/com/dmdirc/addons/logging/ReverseFileReader.java View File

@@ -0,0 +1,261 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.logging;
24
+
25
+import java.io.File;
26
+import java.io.FileNotFoundException;
27
+import java.io.RandomAccessFile;
28
+import java.io.IOException;
29
+import java.io.EOFException;
30
+import java.nio.charset.Charset;
31
+import java.util.Stack;
32
+import java.util.ArrayList;
33
+
34
+/**
35
+ * Reads a file in reverse.
36
+ *
37
+ * @author Shane 'Dataforce' McCormack
38
+ */
39
+public class ReverseFileReader {
40
+
41
+    /** File to manipulate. */
42
+    private RandomAccessFile file;
43
+
44
+    /** Number of bytes to skip backwards at a time. */
45
+    private byte seekLength = 50;
46
+
47
+    /**
48
+     * Create a new ReverseFileReader.
49
+     *
50
+     * @param filename File to open.
51
+     * @throws FileNotFoundException If the file is not a regular file.
52
+     * @throws SecurityException If a security manager exists and its checkRead method denies read access to the file.
53
+     * @throws IOException If there is an error seeking to the end of the file.
54
+     */
55
+    public ReverseFileReader(String filename) throws FileNotFoundException, SecurityException, IOException {
56
+        file = new RandomAccessFile(filename, "r");
57
+        reset();
58
+    }
59
+
60
+    /**
61
+     * Create a new ReverseFileReader.
62
+     *
63
+     * @param myFile Existing file to use.
64
+     * @throws FileNotFoundException If the file is not a regular file.
65
+     * @throws SecurityException If a security manager exists and its checkRead method denies read access to the file.
66
+     * @throws IOException If there is an error seeking to the end of the file.
67
+     */
68
+    public ReverseFileReader(File myFile) throws FileNotFoundException, SecurityException, IOException {
69
+        file = new RandomAccessFile(myFile, "r");
70
+        reset();
71
+    }
72
+
73
+    /**
74
+     * Reset the file pointer to the end of the file.
75
+     *
76
+     * @throws IOException If there is an error seeking, or the file is closed.
77
+     */
78
+    public void reset() throws IOException {
79
+        if (file == null) {
80
+            throw new IOException("File has been closed.");
81
+        }
82
+        file.seek(file.length());
83
+    }
84
+
85
+    /**
86
+     * Get the current seekLength.
87
+     *
88
+     * @return current seekLength
89
+     */
90
+    public byte getSeekLength() {
91
+        return seekLength;
92
+    }
93
+
94
+    /**
95
+     * Set the seekLength.
96
+     *
97
+     * @param newValue New value for seekLength
98
+     */
99
+    public void setSeekLength(final byte newValue) {
100
+        seekLength = newValue;
101
+    }
102
+
103
+    /**
104
+     * Close the file pointer.
105
+     * This should be called before removing the reference to this object
106
+     *
107
+     * @throws IOException If there is an error closing the file, or if it has been closed already.
108
+     */
109
+    public void close() throws IOException {
110
+        if (file == null) {
111
+            throw new IOException("File has been closed.");
112
+        }
113
+        file.close();
114
+        file = null;
115
+    }
116
+
117
+    /**
118
+     * Get the next full line.
119
+     *
120
+     * @return The next full line.
121
+     * @throws EOFException If there is no more lines.
122
+     * @throws IOException If an error reading or seeking occured, or if the fiel is closed.
123
+     */
124
+    public String getNextLine() throws EOFException, IOException {
125
+        if (file == null) {
126
+            throw new IOException("File has been closed.");
127
+        }
128
+        // Used to store result to output.
129
+//		StringBuilder line = new StringBuilder();
130
+        final ArrayList<Byte> line = new ArrayList<Byte>(seekLength);
131
+        // Used to store position in file pre-read
132
+        long fp = 0;
133
+        // Used to store position in file when this is called
134
+        long startfp = 0;
135
+        // Used to store read bytes
136
+        byte[] bytes;
137
+        // Distance seeked
138
+        int seekDistance = 0;
139
+
140
+        // Check current position, if 0 we are at the start of the file
141
+        // and should throw an exception.
142
+        startfp = file.getFilePointer();
143
+        if (startfp == 0) {
144
+            throw new EOFException("Reached Start of file");
145
+        }
146
+
147
+        // Keep looping untill we get a full line, or the end of the file
148
+        boolean keepLooping = true;
149
+        boolean gotNewLine;
150
+        while (keepLooping) {
151
+            gotNewLine = false;
152
+            // Get Current Position
153
+            fp = file.getFilePointer();
154
+
155
+            // Check how far to seek backwards (seekLength or to the start of the file)
156
+            if (fp < seekLength) {
157
+                // Seek to the start of the file;
158
+                seekDistance = (int) fp;
159
+                fp = 0;
160
+            } else {
161
+                // Seek to position current-seekLength
162
+                seekDistance = seekLength;
163
+                fp = fp - seekDistance;
164
+            }
165
+            // Seek!
166
+            file.seek(fp);
167
+
168
+            bytes = new byte[seekDistance];
169
+            // Read into the bytes array
170
+            file.read(bytes);
171
+
172
+            // And loop looking for data
173
+            // This uses seekDistance so that only wanted data is checked.
174
+            for (int i = seekDistance - 1; i >= 0; --i) {
175
+                // Check for New line Character, or a non carraige-return char
176
+                if (bytes[i] == '\n') {
177
+                    // Seek to the location of this character and exit this loop.
178
+                    file.seek(fp + i);
179
+                    gotNewLine = true;
180
+                    break;
181
+                } else if (bytes[i] != '\r') {
182
+                    // Add to the result, the loop will continue going.
183
+                    line.add(0, bytes[i]);
184
+                }
185
+            }
186
+
187
+            // We have now processed the data we read (Either added it all to the
188
+            // buffer, or found a newline character.)
189
+
190
+            if (fp == 0 && !gotNewLine) {
191
+                // This part of the loop started from the start of the file, but didn't
192
+                // find a new line anywhere. no more loops are posssible, so Treat
193
+                // this as "got new line"
194
+                gotNewLine = true;
195
+                file.seek(0);
196
+            }
197
+
198
+            // Do we need to continue?
199
+            if (gotNewLine) {
200
+                // We have found a new line somewhere, thus we don't need
201
+                // to read any more bytes, so exit the while loop!
202
+                keepLooping = false;
203
+            } else {
204
+                // We have not found a new line anywhere,
205
+                // Seek to the pre-read position, and repeat.
206
+                file.seek(fp);
207
+            }
208
+
209
+        }
210
+
211
+        // Return the data obtained.
212
+        byte[] result = new byte[line.size()];
213
+        for (int i = 0; i < line.size(); ++i) {
214
+            result[i] = line.get(i);
215
+        }
216
+        return new String(result, Charset.forName("UTF-8"));
217
+    }
218
+
219
+    /**
220
+     * Try and get x number of lines.
221
+     * If the file is closed, an empty stack will be returned.
222
+     *
223
+     * @param numLines Number of lines to try and get.
224
+     * @return The requested lines
225
+     */
226
+    public Stack<String> getLines(final int numLines) {
227
+        final Stack<String> result = new Stack<String>();
228
+        for (int i = 0; i < numLines; ++i) {
229
+            try {
230
+                result.push(getNextLine());
231
+            } catch (IOException e) {
232
+                break;
233
+            }
234
+        }
235
+        return result;
236
+    }
237
+
238
+    /**
239
+     * Try and get x number of lines and return a \n delimited String.
240
+     * If the file is closed, an empty string will be returned.
241
+     *
242
+     * @param numLines Number of lines to try and get.
243
+     * @return The requested lines
244
+     */
245
+    public String getLinesAsString(final int numLines) {
246
+        final StringBuilder result = new StringBuilder();
247
+        for (int i = 0; i < numLines; ++i) {
248
+            try {
249
+                result.insert(0, "\n");
250
+                result.insert(0, getNextLine());
251
+            } catch (IOException e) {
252
+                break;
253
+            }
254
+        }
255
+        if (result.charAt(0) == '\n') {
256
+            result.deleteCharAt(0);
257
+        }
258
+        return result.toString();
259
+    }
260
+
261
+}

+ 26
- 0
src/com/dmdirc/addons/logging/package-info.java View File

@@ -0,0 +1,26 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+/**
24
+ * Adds logging functionality to the client. 
25
+ */
26
+package com.dmdirc.addons.logging;

+ 49
- 0
src/com/dmdirc/addons/logging/plugin.config View File

@@ -0,0 +1,49 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  requires
9
+  updates
10
+  defaults
11
+  version
12
+
13
+metadata:
14
+  author=Shane <shane@dmdirc.com>
15
+  mainclass=com.dmdirc.addons.logging.LoggingPlugin
16
+  description=Allows logging of conversations
17
+  name=logging
18
+  nicename=Logging Plugin
19
+
20
+requires:
21
+  parent=ui_swing
22
+
23
+updates:
24
+  id=16
25
+
26
+version:
27
+  friendly=0.6
28
+
29
+provides:
30
+  logging command
31
+  logging feature
32
+
33
+required-services:
34
+  swing ui
35
+
36
+defaults:
37
+  general.networkfolders=true
38
+  advanced.filenamehash=false
39
+  general.addtime=true
40
+  general.timestamp=[dd/MM/yyyy HH:mm:ss]
41
+  general.stripcodes=true
42
+  general.channelmodeprefix=true
43
+  backbuffer.autobackbuffer=true
44
+  backbuffer.lines=10
45
+  backbuffer.colour=14
46
+  backbuffer.timestamp=false
47
+  history.lines=50000
48
+  advanced.usedate=false
49
+  advanced.usedateformat=yyyy/MMMM

+ 117
- 0
src/com/dmdirc/addons/mediasource_dcop/AmarokSource.java View File

@@ -0,0 +1,117 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_dcop;
24
+
25
+import com.dmdirc.addons.nowplaying.MediaSource;
26
+import com.dmdirc.addons.nowplaying.MediaSourceState;
27
+
28
+import java.util.List;
29
+
30
+/**
31
+ * Uses DCOP to retrieve now playing info from Amarok.
32
+ *
33
+ * @author chris
34
+ */
35
+public class AmarokSource implements MediaSource {
36
+    
37
+    /** Instantiates the media source. */
38
+    public AmarokSource() {
39
+        //Do nothing
40
+    }
41
+    
42
+    /** {@inheritDoc} */
43
+    @Override
44
+    public MediaSourceState getState() {
45
+        final List<String> res = DcopMediaSourcePlugin.getDcopResult("dcop amarok player status");
46
+        if (res.size() > 0) {
47
+            final String result = res.get(0).trim();
48
+            try {
49
+                final int status = (Integer.parseInt(result));
50
+                switch (status) {
51
+                    case 0:
52
+                        return MediaSourceState.STOPPED;
53
+                    case 1:
54
+                        return MediaSourceState.PAUSED;
55
+                    case 2:
56
+                        return MediaSourceState.PLAYING;
57
+                    default:
58
+                        return MediaSourceState.NOTKNOWN;
59
+                }
60
+            } catch (NumberFormatException nfe) {
61
+                return MediaSourceState.CLOSED;
62
+            }
63
+        } else {
64
+            return MediaSourceState.CLOSED;
65
+        }
66
+    }
67
+    
68
+    /** {@inheritDoc} */
69
+    @Override
70
+    public String getAppName() {
71
+        return "Amarok";
72
+    }
73
+    
74
+    /** {@inheritDoc} */
75
+    @Override
76
+    public String getArtist() {
77
+        return DcopMediaSourcePlugin.getDcopResult("dcop amarok player artist").get(0);
78
+    }
79
+    
80
+    /** {@inheritDoc} */
81
+    @Override
82
+    public String getTitle() {
83
+        return DcopMediaSourcePlugin.getDcopResult("dcop amarok player title").get(0);
84
+    }
85
+    
86
+    /** {@inheritDoc} */
87
+    @Override
88
+    public String getAlbum() {
89
+        return DcopMediaSourcePlugin.getDcopResult("dcop amarok player album").get(0);
90
+    }
91
+    
92
+    /** {@inheritDoc} */
93
+    @Override
94
+    public String getLength() {
95
+        return DcopMediaSourcePlugin.getDcopResult("dcop amarok player totalTime").get(0);
96
+    }
97
+    
98
+    /** {@inheritDoc} */
99
+    @Override
100
+    public String getTime() {
101
+        return DcopMediaSourcePlugin.getDcopResult(
102
+                "dcop amarok player currentTime").get(0);
103
+    }
104
+    
105
+    /** {@inheritDoc} */
106
+    @Override
107
+    public String getFormat() {
108
+        return DcopMediaSourcePlugin.getDcopResult("dcop amarok player type").get(0);
109
+    }
110
+    
111
+    /** {@inheritDoc} */
112
+    @Override
113
+    public String getBitrate() {
114
+        return DcopMediaSourcePlugin.getDcopResult("dcop amarok player bitrate").get(0);
115
+    }
116
+    
117
+}

+ 88
- 0
src/com/dmdirc/addons/mediasource_dcop/DcopMediaSourcePlugin.java View File

@@ -0,0 +1,88 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_dcop;
24
+
25
+import com.dmdirc.addons.nowplaying.MediaSource;
26
+import com.dmdirc.addons.nowplaying.MediaSourceManager;
27
+import com.dmdirc.plugins.NoSuchProviderException;
28
+import com.dmdirc.plugins.Plugin;
29
+import com.dmdirc.plugins.PluginManager;
30
+
31
+import java.util.ArrayList;
32
+import java.util.List;
33
+
34
+/**
35
+ * Manages all DCOP based media sources.
36
+ */
37
+public class DcopMediaSourcePlugin extends Plugin
38
+        implements MediaSourceManager {
39
+    
40
+    /** Media sources. */
41
+    private final List<MediaSource> sources;
42
+    
43
+    /**
44
+     * Creates a new instance of DcopMediaSourcePlugin.
45
+     */
46
+    public DcopMediaSourcePlugin() {
47
+        super();
48
+        sources = new ArrayList<MediaSource>();
49
+        sources.add(new AmarokSource());
50
+        sources.add(new KaffeineSource());
51
+        sources.add(new NoatunSource());
52
+    }
53
+    
54
+    /**
55
+     * Get DCOP Result
56
+     *
57
+     * @param query Query to try
58
+     * @return The result of the dcop query, line-by-line
59
+     */
60
+    @SuppressWarnings("unchecked")
61
+    protected static List<String> getDcopResult(final String query) {
62
+        try {
63
+            return (List<String>) PluginManager.getPluginManager()
64
+                    .getExportedService("dcop").execute(query);
65
+        } catch (NoSuchProviderException nspe) {
66
+            return new ArrayList<String>();
67
+        }
68
+    }
69
+    
70
+    /** {@inheritDoc} */
71
+    @Override
72
+    public List<MediaSource> getSources() {
73
+        return sources;
74
+    }
75
+    
76
+    /** {@inheritDoc} */
77
+    @Override
78
+    public void onLoad() {
79
+        // Nothing to do
80
+    }
81
+    
82
+    /** {@inheritDoc} */
83
+    @Override
84
+    public void onUnload() {
85
+        // Nothing to do
86
+    }
87
+
88
+}

+ 147
- 0
src/com/dmdirc/addons/mediasource_dcop/KaffeineSource.java View File

@@ -0,0 +1,147 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_dcop;
24
+
25
+import com.dmdirc.addons.nowplaying.MediaSource;
26
+import com.dmdirc.addons.nowplaying.MediaSourceState;
27
+
28
+import java.util.List;
29
+
30
+/**
31
+ * Uses DCOP to retrieve now playing info from Kaffeine.
32
+ *
33
+ * @author chris
34
+ */
35
+public class KaffeineSource implements MediaSource {
36
+    
37
+    /** Instantiates the media source. */
38
+    public KaffeineSource() {
39
+        //Do nothing
40
+    }
41
+    
42
+    /** {@inheritDoc} */
43
+    @Override
44
+    public MediaSourceState getState() {
45
+        final List<String> res = DcopMediaSourcePlugin.getDcopResult("dcop kaffeine KaffeineIface isPlaying");
46
+        if (res.size() > 0) {
47
+            final String result = res.get(0);
48
+            if (Boolean.parseBoolean(result)) {
49
+                return MediaSourceState.PLAYING;
50
+            } else {
51
+                return MediaSourceState.CLOSED;
52
+            }
53
+        } else {
54
+            return MediaSourceState.CLOSED;
55
+        }
56
+    }
57
+    
58
+    /** {@inheritDoc} */
59
+    @Override
60
+    public String getAppName() {
61
+        return "Kaffeine";
62
+    }
63
+    
64
+    /** {@inheritDoc} */
65
+    @Override
66
+    public String getArtist() {
67
+        return DcopMediaSourcePlugin.getDcopResult(
68
+                "dcop kaffeine KaffeineIface artist").get(0);
69
+    }
70
+
71
+    /** {@inheritDoc} */
72
+    @Override
73
+    public String getTitle() {
74
+        return DcopMediaSourcePlugin.getDcopResult(
75
+                "dcop kaffeine KaffeineIface title").get(0);
76
+    }
77
+
78
+    /** {@inheritDoc} */
79
+    @Override
80
+    public String getAlbum() {
81
+        return DcopMediaSourcePlugin.getDcopResult(
82
+                "dcop kaffeine KaffeineIface album").get(0);
83
+    }
84
+
85
+    /** {@inheritDoc} */
86
+    @Override
87
+    public String getLength() {
88
+        return duration(Integer.parseInt(DcopMediaSourcePlugin.getDcopResult(
89
+                "dcop kaffeine KaffeineIface getLength").get(0)));
90
+    }
91
+
92
+    /** {@inheritDoc} */
93
+    @Override
94
+    public String getTime() {
95
+        return duration(Integer.parseInt(DcopMediaSourcePlugin.getDcopResult(
96
+                "dcop kaffeine KaffeineIface getTimePos").get(0)));
97
+    }
98
+
99
+    /** {@inheritDoc} */
100
+    @Override
101
+    public String getFormat() {
102
+        return null;
103
+    }
104
+
105
+    /** {@inheritDoc} */
106
+    @Override
107
+    public String getBitrate() {
108
+        return null;
109
+    }
110
+    
111
+    /**
112
+     * Get the duration in seconds as a string.
113
+     *
114
+     * @param secondsInput to get duration for
115
+     *
116
+     * @return Duration as a string
117
+     */
118
+    private String duration(final long secondsInput) {
119
+        final StringBuilder result = new StringBuilder();
120
+        final long hours = secondsInput / 3600;
121
+        final long minutes = secondsInput / 60 % 60;
122
+        final long seconds = secondsInput % 60;
123
+        
124
+        if (hours > 0) {
125
+            if (hours < 10) {
126
+                result.append('0');
127
+            }
128
+
129
+            result.append(hours).append(":");
130
+        }
131
+
132
+        if (minutes < 10) {
133
+            result.append('0');
134
+        }
135
+        
136
+        result.append(minutes).append(":");
137
+
138
+        if (seconds < 10) {
139
+            result.append('0');
140
+        }
141
+
142
+        result.append(seconds);
143
+        
144
+        return result.toString();
145
+    }
146
+    
147
+}

+ 151
- 0
src/com/dmdirc/addons/mediasource_dcop/NoatunSource.java View File

@@ -0,0 +1,151 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_dcop;
24
+
25
+import com.dmdirc.addons.nowplaying.MediaSource;
26
+import com.dmdirc.addons.nowplaying.MediaSourceState;
27
+
28
+import java.util.List;
29
+
30
+/**
31
+ * Uses DCOP to retrieve now playing info from Noatun.
32
+ *
33
+ * @author chris
34
+ */
35
+public class NoatunSource implements MediaSource {
36
+    
37
+    /** Instantiates the media source. */
38
+    public NoatunSource() {
39
+        //Do nothing
40
+    }
41
+    
42
+    /** {@inheritDoc} */
43
+    @Override
44
+    public MediaSourceState getState() {
45
+        final List<String> res = DcopMediaSourcePlugin.getDcopResult("dcop noatun Noatun state");
46
+        if (res.size() > 0) {
47
+            final String result = res.get(0).trim();
48
+            try {
49
+                final int status = (Integer.parseInt(result));
50
+                switch (status) {
51
+                    case 0:
52
+                        return MediaSourceState.STOPPED;
53
+                    case 1:
54
+                        return MediaSourceState.PAUSED;
55
+                    case 2:
56
+                        return MediaSourceState.PLAYING;
57
+                    default:
58
+                        return MediaSourceState.NOTKNOWN;
59
+                }
60
+            } catch (NumberFormatException nfe) {
61
+                return MediaSourceState.CLOSED;
62
+            }
63
+        } else {
64
+            return MediaSourceState.CLOSED;
65
+        }
66
+    }
67
+    
68
+    /** {@inheritDoc} */
69
+    @Override
70
+    public String getAppName() {
71
+        return "Noatun";
72
+    }
73
+    
74
+    /** {@inheritDoc} */
75
+    @Override
76
+    public String getArtist() {
77
+        final String result = DcopMediaSourcePlugin.getDcopResult(
78
+                "dcop noatun Noatun title").get(0);
79
+        if (result.indexOf(" - ") == -1) {
80
+            return "";
81
+        }
82
+        return result.substring(0, result.indexOf(" - "));
83
+    }
84
+
85
+    /** {@inheritDoc} */
86
+    @Override
87
+    public String getTitle() {
88
+        final String result = DcopMediaSourcePlugin.getDcopResult(
89
+                "dcop noatun Noatun title").get(0);
90
+        if (result.indexOf(" - ") == -1) {
91
+            return "";
92
+        }
93
+        return result.substring(result.indexOf(" - ") + 3, result.length());
94
+    }
95
+
96
+    /** {@inheritDoc} */
97
+    @Override
98
+    public String getAlbum() {
99
+        return null;
100
+    }
101
+
102
+    /** {@inheritDoc} */
103
+    @Override
104
+    public String getLength() {
105
+        return DcopMediaSourcePlugin.getDcopResult(
106
+                "dcop noatun Noatun lengthString").get(0);
107
+    }
108
+
109
+    /** {@inheritDoc} */
110
+    @Override
111
+    public String getTime() {
112
+        return duration(Integer.parseInt(DcopMediaSourcePlugin.getDcopResult(
113
+                "dcop noatun Noatun position").get(0)) /1000);
114
+    }
115
+
116
+    /** {@inheritDoc} */
117
+    @Override
118
+    public String getFormat() {
119
+        return null;
120
+    }
121
+
122
+    /** {@inheritDoc} */
123
+    @Override
124
+    public String getBitrate() {
125
+        return null;
126
+    }
127
+    
128
+    /**
129
+     * Get the duration in seconds as a string.
130
+     *
131
+     * @param secondsInput to get duration for
132
+     *
133
+     * @return Duration as a string
134
+     */
135
+    private String duration(final long secondsInput) {
136
+        final StringBuilder result = new StringBuilder();
137
+        final long hours = secondsInput / 3600;
138
+        final long minutes = secondsInput / 60 % 60;
139
+        final long seconds = secondsInput % 60;
140
+        
141
+        if (hours > 0) { 
142
+            result.append(hours).append(":");
143
+        }
144
+        
145
+        result.append(minutes).append(":");
146
+        result.append(seconds).append(":");
147
+        
148
+        return result.toString();
149
+    }
150
+    
151
+}

+ 31
- 0
src/com/dmdirc/addons/mediasource_dcop/plugin.config View File

@@ -0,0 +1,31 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  updates
9
+  version
10
+
11
+metadata:
12
+  author=Greboid <greg@dmdirc.com>
13
+  mainclass=com.dmdirc.addons.mediasource_dcop.DcopMediaSourcePlugin
14
+  description=Provides DCOP media sources for the now playing plugin
15
+  name=dcopmediasource
16
+  nicename=DCOP Media Sources
17
+
18
+updates:
19
+  id=9
20
+
21
+version:
22
+  friendly=0.3
23
+
24
+provides:
25
+  amarok mediasource
26
+  kaffeine mediasource
27
+  noatum mediasource
28
+
29
+required-services:
30
+  dcop export
31
+  mediasource manager

+ 70
- 0
src/com/dmdirc/addons/mediasource_vlc/InstructionsPanel.java View File

@@ -0,0 +1,70 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_vlc;
24
+
25
+import com.dmdirc.config.prefs.PreferencesInterface;
26
+import com.dmdirc.addons.ui_swing.components.text.TextLabel;
27
+
28
+import javax.swing.JPanel;
29
+
30
+import net.miginfocom.swing.MigLayout;
31
+
32
+/**
33
+ * Shows installation instructions for the VLC media source.
34
+ * 
35
+ * @author chris
36
+ */
37
+class InstructionsPanel extends JPanel implements PreferencesInterface {
38
+    
39
+    private static final long serialVersionUID = 1;
40
+
41
+    public InstructionsPanel() {
42
+        setLayout(new MigLayout());
43
+        final TextLabel instructions = new TextLabel("<html><p>"                
44
+                + "The VLC media source requires that VLC's web interface is" +
45
+                " enabled. To do this, follow the steps below:</p>"
46
+                + "<ol style='margin-left: 20px; padding-left: 0px;'>" +
47
+                "<li>Open VLC's preferences dialog (found in the 'Tools' menu)" +
48
+                "<li>Set the 'Show settings' option to 'All'" +
49
+                "<li>Expand the 'Interface' category by clicking on the plus sign next to it" +
50
+                "<li>Select the 'Main interfaces' category" +
51
+                "<li>Check the box next to 'HTTP remote control interface'" +
52
+                "<li>Expand the 'Main interfaces' category" +
53
+                "<li>Select the 'HTTP' category" +
54
+                "<li>In the 'Host address' field, enter 'localhost:8082'" +
55
+                "<li>In the 'Source directory' field enter the path to VLC's" +
56
+                " http directory<ul style='margin-left: 5px; padding-left: 0px;" +
57
+                "list-style-type: none;'>" +
58
+                "<li style='padding-bottom: 5px'>For Linux users this may be /usr/share/vlc/http/" +
59
+                "<li>For Windows users this will be under the main VLC directory, e.g. " +
60
+                "C:\\Program Files\\VLC\\http</ul><li>Click 'Save'<li>Restart VLC</ol></html>");
61
+        add(instructions);
62
+    }
63
+
64
+    /** {@inheritDpc} */
65
+    @Override
66
+    public void save() {
67
+        // Do nothing
68
+    }
69
+
70
+}

+ 310
- 0
src/com/dmdirc/addons/mediasource_vlc/VlcMediaSourcePlugin.java View File

@@ -0,0 +1,310 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_vlc;
24
+
25
+import com.dmdirc.addons.nowplaying.MediaSource;
26
+import com.dmdirc.addons.nowplaying.MediaSourceState;
27
+import com.dmdirc.config.IdentityManager;
28
+import com.dmdirc.config.prefs.PreferencesCategory;
29
+import com.dmdirc.config.prefs.PreferencesManager;
30
+import com.dmdirc.config.prefs.PreferencesSetting;
31
+import com.dmdirc.config.prefs.PreferencesType;
32
+import com.dmdirc.plugins.Plugin;
33
+import com.dmdirc.util.Downloader;
34
+
35
+import java.io.File;
36
+import java.io.IOException;
37
+import java.net.MalformedURLException;
38
+import java.util.HashMap;
39
+import java.util.List;
40
+import java.util.Map;
41
+
42
+/**
43
+ * Retrieves information from VLC using its HTTP interface.
44
+ * 
45
+ * @author chris
46
+ */
47
+public class VlcMediaSourcePlugin extends Plugin implements MediaSource {
48
+    
49
+    /** The information obtained from VLC. */
50
+    private final Map<String, String> information
51
+            = new HashMap<String, String>();
52
+
53
+    /** {@inheritDoc} */
54
+    @Override
55
+    public MediaSourceState getState() {
56
+        if (fetchInformation()) {
57
+            final String output = information.get("state");
58
+            if (output.equalsIgnoreCase("stop")) {
59
+                return MediaSourceState.STOPPED;
60
+            } else if (output.equalsIgnoreCase("playing")) {
61
+                return MediaSourceState.PLAYING;
62
+            } else if (output.equalsIgnoreCase("paused")) {
63
+                return MediaSourceState.PAUSED;
64
+            } else {
65
+                return MediaSourceState.NOTKNOWN;
66
+            }
67
+        } else {
68
+            return MediaSourceState.CLOSED;
69
+        }
70
+    }
71
+
72
+    /** {@inheritDoc} */
73
+    @Override    
74
+    public String getAppName() {
75
+        return "VLC";
76
+    }
77
+
78
+    /** {@inheritDoc} */
79
+    @Override    
80
+    public String getArtist() {
81
+        return information.containsKey("artist") ? information.get("artist") :
82
+                getFallbackArtist();
83
+    }
84
+     
85
+    /**
86
+     * Retrieves the fallback artist (parsed from the file name).
87
+     * 
88
+     * @return The fallback artist
89
+     */
90
+    private String getFallbackArtist() {
91
+        String result = "unknown";
92
+        
93
+        if (information.containsKey("playlist_current")) {
94
+            try {
95
+                final int item = Integer.parseInt(information.get("playlist_current"));
96
+                String[] bits = information.get("playlist_item_" + item).split(
97
+                        (File.separatorChar=='\\' ? "\\\\" : File.separator));
98
+                result = bits[bits.length-1];
99
+                bits = result.split("-");
100
+                if (bits.length > 1) {
101
+                    result = bits[0];
102
+                } else {
103
+                    // Whole filename is the title, so no artist is known.
104
+                    result = "unknown";
105
+                }
106
+            } catch (NumberFormatException nfe) {
107
+                // DO nothing
108
+            }
109
+        }
110
+        
111
+        return result;
112
+    }
113
+
114
+    /** {@inheritDoc} */
115
+    @Override    
116
+    public String getTitle() {
117
+        return information.containsKey("title") ? information.get("title")
118
+                : getFallbackTitle();
119
+    }
120
+    
121
+    /**
122
+     * Retrieves the fallback title (parsed from the file name).
123
+     * 
124
+     * @return The fallback title
125
+     */
126
+    private String getFallbackTitle() {
127
+        String result = "unknown";
128
+        
129
+        // Title is unknown, lets guess using the filename
130
+        if (information.containsKey("playlist_current")) {
131
+            try {
132
+                final int item = Integer.parseInt(information.get("playlist_current"));
133
+                result = information.get("playlist_item_" + item);
134
+                
135
+                final int sepIndex = result.lastIndexOf(File.separatorChar);
136
+                final int extIndex = result.lastIndexOf('.');
137
+                result = result.substring(sepIndex,
138
+                        extIndex > sepIndex ? extIndex : result.length());
139
+                
140
+                final int offset = result.indexOf('-');
141
+                if (offset > -1) {
142
+                    result = result.substring(offset + 1).trim();
143
+                }
144
+            } catch (NumberFormatException nfe) {
145
+                // Do nothing
146
+            }
147
+        }
148
+        
149
+        return result;
150
+    }
151
+
152
+    /** {@inheritDoc} */
153
+    @Override    
154
+    public String getAlbum() {
155
+        return information.containsKey("album/movie/show title")
156
+                ? information.get("album/movie/show title") : "unknown";
157
+    }
158
+
159
+    /** {@inheritDoc} */
160
+    @Override    
161
+    public String getLength() {
162
+        // This is just seconds, could do with formatting.
163
+        return information.containsKey("length") ? information.get("length")
164
+                : "unknown";
165
+    }
166
+
167
+    /** {@inheritDoc} */
168
+    @Override    
169
+    public String getTime() {
170
+        // This is just seconds, could do with formatting.
171
+        return information.containsKey("time") ? information.get("time")
172
+                : "unknown";
173
+    }
174
+
175
+    /** {@inheritDoc} */
176
+    @Override    
177
+    public String getFormat() {
178
+        return information.containsKey("codec") ? information.get("codec")
179
+                : "unknown";
180
+    }
181
+
182
+    /** {@inheritDoc} */
183
+    @Override    
184
+    public String getBitrate() {
185
+        return information.containsKey("bitrate") ? information.get("bitrate")
186
+                : "unknown";
187
+    }
188
+
189
+    /** {@inheritDoc} */
190
+    @Override    
191
+    public void onLoad() {
192
+        // Do nothing
193
+    }
194
+
195
+    /** {@inheritDoc} */
196
+    @Override    
197
+    public void onUnload() {
198
+        // Do nothing
199
+    }
200
+
201
+    /** {@inheritDoc} */
202
+    @Override
203
+    public void showConfig(final PreferencesManager manager) {
204
+        final PreferencesCategory general = new PreferencesCategory("VLC Media Source",
205
+                "", "category-vlc");
206
+        final PreferencesCategory instr = new PreferencesCategory("Instructions",
207
+                "", new InstructionsPanel());
208
+        
209
+        general.addSetting(new PreferencesSetting(PreferencesType.TEXT, 
210
+                getDomain(), "host", "Hostname and port",
211
+                "The host and port that VLC listens on for web connections"));
212
+        
213
+        manager.getCategory("Plugins").addSubCategory(general);
214
+        general.addSubCategory(instr.setInline());
215
+    }
216
+    
217
+    /**
218
+     * Attempts to fetch information from VLC's web interface.
219
+     * 
220
+     * @return True on success, false otherwise
221
+     */
222
+    private boolean fetchInformation() {
223
+        information.clear();
224
+        List<String> res;
225
+        List<String> res2;
226
+        
227
+        try {
228
+            res = Downloader.getPage("http://" +
229
+                    IdentityManager.getGlobalConfig().getOption(getDomain(),
230
+                    "host") + "/old/info.html");
231
+            res2 = Downloader.getPage("http://" +
232
+                    IdentityManager.getGlobalConfig().getOption(getDomain(),
233
+                    "host") + "/old/");
234
+        } catch (MalformedURLException ex) {
235
+            return false;
236
+        } catch (IOException ex) {
237
+            return false;
238
+        }
239
+        
240
+        parseInformation(res, res2);
241
+        
242
+        return true;
243
+    }
244
+    
245
+    /**
246
+     * Parses the information from the two pages obtained from VLC's web
247
+     * interface.
248
+     * 
249
+     * @param res The first page of VLC info (/old/info.html)
250
+     * @param res2 The second page of VLC info (/old/)
251
+     */
252
+    protected void parseInformation(final List<String> res,
253
+            final List<String> res2) {
254
+        for (String line : res) {
255
+            final String tline = line.trim();
256
+            
257
+            if (tline.startsWith("<li>")) {
258
+                final int colon = tline.indexOf(':');
259
+                final String key = tline.substring(5, colon).trim().toLowerCase();
260
+                final String value = tline.substring(colon + 1, tline.length() - 5).trim();
261
+                
262
+                information.put(key, value);
263
+            }
264
+        }
265
+        
266
+        boolean isPlaylist = false;
267
+        boolean isCurrent = false;
268
+        boolean isItem = false;
269
+        int playlistItem = 0;
270
+        for (String line : res2) {
271
+            final String tline = line.trim();
272
+            
273
+            if (isPlaylist) {
274
+                if (tline.startsWith("</ul>")) {
275
+                    isPlaylist = false;
276
+                    information.put("playlist_items", Integer.toString(playlistItem));
277
+                } else if (tline.equalsIgnoreCase("<strong>")) {
278
+                    isCurrent = true;
279
+                } else if (tline.equalsIgnoreCase("</strong>")) {
280
+                    isCurrent = false;
281
+                } else if (tline.startsWith("<a href=\"?control=play&amp")) {
282
+                    isItem = true;
283
+                } else if (isItem) {
284
+                    String itemname = tline;
285
+                    if (itemname.endsWith("</a>")) {
286
+                        itemname = itemname.substring(0, itemname.length()-4);
287
+                    }
288
+                    if (!itemname.isEmpty()) {
289
+                        if (isCurrent) {
290
+                            information.put("playlist_current", Integer.toString(playlistItem));
291
+                        }
292
+                        information.put("playlist_item_"+Integer.toString(playlistItem++),
293
+                                itemname);
294
+                    }
295
+                    isItem = false;
296
+                }
297
+            } else if (tline.equalsIgnoreCase("<!-- Playlist -->")) {
298
+                isPlaylist = true;
299
+            } else if (tline.startsWith("State:")) {
300
+                information.put("state", tline.substring(6, tline.indexOf('<')).trim());
301
+            } else if (tline.startsWith("got_")) {
302
+                final int equals = tline.indexOf('=');
303
+                
304
+                information.put(tline.substring(4, equals).trim(),
305
+                        tline.substring(equals + 1, tline.length() - 1).trim());
306
+            }
307
+        }
308
+    }
309
+
310
+}

+ 40
- 0
src/com/dmdirc/addons/mediasource_vlc/plugin.config View File

@@ -0,0 +1,40 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  requires
9
+  updates
10
+  version
11
+  defaults
12
+  icons
13
+
14
+metadata:
15
+  author=Chris <chris@dmdirc.com>
16
+  mainclass=com.dmdirc.addons.mediasource_vlc.VlcMediaSourcePlugin
17
+  description=Provides VLC nowplaying information
18
+  name=vlcmediasource
19
+  nicename=VLC Media Sources
20
+
21
+requires:
22
+  dmdirc=4641
23
+
24
+updates:
25
+  id=24
26
+
27
+version:
28
+  friendly=0.3
29
+
30
+provides:
31
+  vlc mediasource
32
+
33
+required-services:
34
+  mediasource manager
35
+
36
+defaults:
37
+  host=localhost:8082
38
+
39
+icons:
40
+  category-vlc=plugin://vlcmediasource:com/dmdirc/addons/mediasource_vlc/vlc.png

BIN
src/com/dmdirc/addons/mediasource_vlc/vlc.png View File


+ 180
- 0
src/com/dmdirc/addons/mediasource_windows/DllSource.java View File

@@ -0,0 +1,180 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_windows;
24
+
25
+import com.dmdirc.addons.nowplaying.MediaSource;
26
+import com.dmdirc.addons.nowplaying.MediaSourceState;
27
+
28
+/**
29
+ * Uses WindowsMediaSourcePlugin to retrieve now playing info.
30
+ *
31
+ * @author Shane
32
+ */
33
+public class DllSource implements MediaSource {
34
+
35
+    /** Player name */
36
+    private final String playerName;
37
+
38
+    /** Use getArtistTitle */
39
+    private final boolean useArtistTitle;
40
+
41
+    /**
42
+     * Instantiates the media source.
43
+     *
44
+     * @param playerName Name of Player and DLL
45
+     */
46
+    public DllSource(final String playerName) {
47
+        this(playerName, false);
48
+    }
49
+
50
+    /**
51
+     * Instantiates the media source.
52
+     *
53
+     * @param playerName Name of Player and DLL
54
+     * @param useArtistTitle True if getArtistTitle should be parsed rather than
55
+     *                       using getArtist() and getTitle()
56
+     */
57
+    public DllSource(final String playerName, final boolean useArtistTitle) {
58
+        this.playerName = playerName;
59
+        this.useArtistTitle = useArtistTitle;
60
+    }
61
+
62
+    /** {@inheritDoc} */
63
+    @Override
64
+    public String getAppName() {
65
+        return playerName;
66
+    }
67
+
68
+    /**
69
+     * Get the "goodoutput" from GetMediaInfo for the given command
70
+     *
71
+     * @param command Command to run
72
+     * @return "Good" Output
73
+     */
74
+    private String getOutput(final String command) {
75
+        return WindowsMediaSourcePlugin.getOutput(playerName, command).getGoodOutput();
76
+    }
77
+
78
+    /** {@inheritDoc} */
79
+    @Override
80
+    public MediaSourceState getState() {
81
+        final MediaInfoOutput result = WindowsMediaSourcePlugin.getOutput(playerName, "getPlayState");
82
+
83
+        if (result.getExitCode() == 0) {
84
+            final String output = result.getGoodOutput();
85
+            if (output.equalsIgnoreCase("stopped")) {
86
+                return MediaSourceState.STOPPED;
87
+            } else if (output.equalsIgnoreCase("playing")) {
88
+                return MediaSourceState.PLAYING;
89
+            } else if (output.equalsIgnoreCase("paused")) {
90
+                return MediaSourceState.PAUSED;
91
+            } else {
92
+                return MediaSourceState.NOTKNOWN;
93
+            }
94
+        } else {
95
+            return MediaSourceState.CLOSED;
96
+        }
97
+    }
98
+
99
+    /** {@inheritDoc} */
100
+    @Override
101
+    public String getArtist() {
102
+        if (useArtistTitle) {
103
+            return getOutput("getArtistTitle").split("\\s-\\s", 2)[0];
104
+        } else {
105
+            return getOutput("getArtist");
106
+        }
107
+    }
108
+
109
+    /** {@inheritDoc} */
110
+    @Override
111
+    public String getTitle() {
112
+        if (useArtistTitle) {
113
+            String bits[] = getOutput("getArtistTitle").split("\\s-\\s", 2);
114
+            return (bits.length > 1) ? bits[1] : "";
115
+        } else {
116
+            return getOutput("getTitle");
117
+        }
118
+    }
119
+
120
+    /** {@inheritDoc} */
121
+    @Override
122
+    public String getAlbum() {
123
+        return getOutput("getAlbum");
124
+    }
125
+
126
+    /**
127
+     * Get the duration in seconds as a string.
128
+     *
129
+     * @param secondsInput to get duration for
130
+     * @return Duration as a string
131
+     */
132
+    private String duration(final long secondsInput) {
133
+        final StringBuilder result = new StringBuilder();
134
+        final long hours = (secondsInput / 3600);
135
+        final long minutes = (secondsInput / 60 % 60);
136
+        final long seconds = (secondsInput % 60);
137
+
138
+        if (hours > 0) {
139
+            result.append(hours + ":");
140
+        }
141
+        result.append(String.format("%0,2d:%0,2d", minutes, seconds));
142
+
143
+        return result.toString();
144
+    }
145
+
146
+    /** {@inheritDoc} */
147
+    @Override
148
+    public String getLength() {
149
+        try {
150
+            final int seconds = Integer.parseInt(getOutput("getLength"));
151
+            return duration(seconds);
152
+        } catch (NumberFormatException nfe) {
153
+        }
154
+        return "Unknown";
155
+    }
156
+
157
+    /** {@inheritDoc} */
158
+    @Override
159
+    public String getTime() {
160
+        try {
161
+            final int seconds = Integer.parseInt(getOutput("getTime"));
162
+            return duration(seconds);
163
+        } catch (NumberFormatException nfe) {
164
+        }
165
+        return "Unknown";
166
+    }
167
+
168
+    /** {@inheritDoc} */
169
+    @Override
170
+    public String getFormat() {
171
+        return getOutput("getFormat");
172
+    }
173
+
174
+    /** {@inheritDoc} */
175
+    @Override
176
+    public String getBitrate() {
177
+        return getOutput("getBitrate");
178
+    }
179
+
180
+}

+ 74
- 0
src/com/dmdirc/addons/mediasource_windows/MediaInfoOutput.java View File

@@ -0,0 +1,74 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_windows;
24
+
25
+/**
26
+ * Holds Output from GetMediaInfo.exe
27
+ */
28
+public class MediaInfoOutput {
29
+
30
+    /** Exit Code from GetMediaInfo.exe */
31
+    private final int exitCode;
32
+
33
+    /** Output from GetMediaInfo.exe */
34
+    private final String output;
35
+
36
+    /**
37
+     * Create a MediaInfoOutput
38
+     *
39
+     * @param exitCode Exit code from GetMediaInfo.exe
40
+     * @param output Output from GetMediaInfo.exe
41
+     */
42
+    public MediaInfoOutput(final int exitCode, final String output) {
43
+        this.exitCode = exitCode;
44
+        this.output = output;
45
+    }
46
+
47
+    /**
48
+     * Get the exit code
49
+     *
50
+     * @return exit Code
51
+     */
52
+    public int getExitCode() {
53
+        return exitCode;
54
+    }
55
+
56
+    /**
57
+     * Get the output
58
+     *
59
+     * @return output
60
+     */
61
+    public String getOutput() {
62
+        return output;
63
+    }
64
+
65
+    /**
66
+     * Get the output only if the exitCode was 0, else a blank string.
67
+     *
68
+     * @return The output only if the exitCode was 0, else a blank string.
69
+     */
70
+    public String getGoodOutput() {
71
+        return (getExitCode() == 0) ? output : "";
72
+    }
73
+
74
+}

+ 158
- 0
src/com/dmdirc/addons/mediasource_windows/WindowsMediaSourcePlugin.java View File

@@ -0,0 +1,158 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.mediasource_windows;
24
+
25
+import com.dmdirc.Main;
26
+import com.dmdirc.addons.nowplaying.MediaSource;
27
+import com.dmdirc.addons.nowplaying.MediaSourceManager;
28
+import com.dmdirc.logger.ErrorLevel;
29
+import com.dmdirc.logger.Logger;
30
+import com.dmdirc.plugins.Plugin;
31
+import com.dmdirc.plugins.PluginInfo;
32
+import com.dmdirc.plugins.PluginManager;
33
+import com.dmdirc.util.resourcemanager.ResourceManager;
34
+
35
+import java.util.ArrayList;
36
+import java.util.List;
37
+import java.util.Map;
38
+import java.util.Map.Entry;
39
+import java.io.File;
40
+import java.io.IOException;
41
+
42
+import com.dmdirc.installer.StreamReader;
43
+
44
+/**
45
+ * Manages all Windows based media sources.
46
+ */
47
+public class WindowsMediaSourcePlugin extends Plugin implements MediaSourceManager {
48
+
49
+    /** Media sources. */
50
+    private final List<MediaSource> sources;
51
+
52
+    /** Files dir */
53
+    private static final String filesDir = Main.getConfigDir() + "plugins/windowsmediasource_files/";
54
+
55
+    /**
56
+     * Creates a new instance of DcopMediaSourcePlugin.
57
+     */
58
+    public WindowsMediaSourcePlugin() {
59
+        super();
60
+        sources = new ArrayList<MediaSource>();
61
+        sources.add(new DllSource("Winamp", true));
62
+        sources.add(new DllSource("iTunes", false));
63
+    }
64
+
65
+    /** {@inheritDoc} */
66
+    @Override
67
+    public List<MediaSource> getSources() {
68
+        return sources;
69
+    }
70
+
71
+    /**
72
+     * Get the output from GetMediaInfo.exe for the given player and method
73
+     *
74
+     * @param player Player to ask about
75
+     * @param method Method to call
76
+     * @return a MediaInfoOutput with the results
77
+     */
78
+    protected static MediaInfoOutput getOutput(final String player, final String method) {
79
+        try {
80
+            final Process myProcess = Runtime.getRuntime().exec(new String[]{filesDir + "GetMediaInfo.exe", player, method});
81
+            final StringBuffer data = new StringBuffer();
82
+            new StreamReader(myProcess.getErrorStream()).start();
83
+            new StreamReader(myProcess.getInputStream(), data).start();
84
+            try {
85
+                myProcess.waitFor();
86
+            } catch (InterruptedException e) {
87
+            }
88
+
89
+            return new MediaInfoOutput(myProcess.exitValue(), data.toString());
90
+        } catch (SecurityException e) {
91
+        } catch (IOException e) {
92
+        }
93
+
94
+        return new MediaInfoOutput(-1, "Error executing GetMediaInfo.exe");
95
+    }
96
+
97
+    /**
98
+     * Use the given resource manager to extract files ending with the given suffix
99
+     *
100
+     * @param res ResourceManager
101
+     * @param newDir Directory to extract to.
102
+     * @param suffix Suffix to extract
103
+     */
104
+    private void extractFiles(final ResourceManager res, final File newDir, final String suffix) {
105
+        final Map<String, byte[]> resources = res.getResourcesEndingWithAsBytes(suffix);
106
+        for (Entry<String, byte[]> resource : resources.entrySet()) {
107
+            try {
108
+                final String key = resource.getKey();
109
+                final String resourceName = key.substring(key.lastIndexOf('/'), key.length());
110
+
111
+                final File newFile = new File(newDir, resourceName);
112
+
113
+                if (!newFile.isDirectory()) {
114
+                    if (newFile.exists()) {
115
+                        newFile.delete();
116
+                    }
117
+                    ResourceManager.getResourceManager().resourceToFile(resource.getValue(), newFile);
118
+                }
119
+            } catch (IOException ex) {
120
+                Logger.userError(ErrorLevel.LOW, "Failed to extract " + suffix + "s for windowsmediasource: " + ex.getMessage(), ex);
121
+            }
122
+        }
123
+    }
124
+
125
+    /** {@inheritDoc} */
126
+    @Override
127
+    public void onLoad() {
128
+        // Extract GetMediaInfo.exe and required DLLs
129
+        final PluginInfo pi = PluginManager.getPluginManager().getPluginInfoByName("windowsmediasource");
130
+
131
+        // This shouldn't actually happen, but check to make sure.
132
+        if (pi == null) {
133
+            return;
134
+        }
135
+
136
+        // Now get the RM
137
+        try {
138
+            final ResourceManager res = pi.getResourceManager();
139
+
140
+            // Make sure our files dir exists
141
+            final File newDir = new File(filesDir);
142
+            if (!newDir.exists()) {
143
+                newDir.mkdirs();
144
+            }
145
+
146
+            // Now extract the .dlls and .exe
147
+            extractFiles(res, newDir, ".dll");
148
+            extractFiles(res, newDir, ".exe");
149
+        } catch (IOException ioe) {
150
+            Logger.userError(ErrorLevel.LOW, "Unable to open ResourceManager for windowsmediasource: " + ioe.getMessage(), ioe);
151
+        }
152
+    }
153
+
154
+    /** {@inheritDoc} */
155
+    @Override
156
+    public void onUnload() { /* Do Nothing */ }
157
+
158
+}

+ 91
- 0
src/com/dmdirc/addons/mediasource_windows/files/GetMediaInfo.dpr View File

@@ -0,0 +1,91 @@
1
+{*
2
+ * DMDirc - Open Source IRC Client
3
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
4
+ * 
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+ * 
12
+ * The above copyright notice and this permission notice shall be included in
13
+ * all copies or substantial portions of the Software.
14
+ * 
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ * SOFTWARE.
22
+ *
23
+ *
24
+ * This application attempts to load a dll for media source information.
25
+ * It can be run from the commandline like so:
26
+ *  - GetMediaInfo.exe winamp getArtist
27
+ * and it will load the winamp dll and attempt to execute the function "artist"
28
+ * Any output will be echoed to the command line with an exit code of 0.
29
+ * An exit code of 1 means there was an error from the DLL
30
+ * An exit code of 2 means there was an error before the DLL
31
+ *
32
+ * Methods are very simple:
33
+ *   Method: function (data: PChar): integer; stdcall;
34
+ * A pchar of size 1024 is passed, resulting data should be written to this
35
+ * pchar inside the dll.
36
+ * The method should then return either 0 to indicate success, or 1 to indicate
37
+ * an error. (This will be used as the exit code, and the contents of Data is 
38
+ * printed to stdout - any returned value > 1 will still give an exitcode of 1)
39
+ *}
40
+program GetMediaInfo;
41
+{$MODE Delphi}
42
+
43
+uses Windows, sysutils;
44
+var
45
+	I: Integer;
46
+	Player: String;
47
+	MethodName: String;
48
+	DLL: HINST;
49
+	
50
+	Method: function (data: PChar): integer; stdcall;
51
+	Data: PChar;
52
+begin
53
+	// By default, assume fail.
54
+	ExitCode := 2;
55
+
56
+	if ParamCount > 1 then begin
57
+		// Params
58
+		Player := paramstr(1);
59
+		MethodName := paramstr(2);
60
+		
61
+		// Load the DLL
62
+		DLL := LoadLibrary(PChar(Player+'.dll'));
63
+		
64
+		if DLL <> HINST(0) then begin
65
+			// DLL Exists and is loaded
66
+			// Now the actual method we want!
67
+			Method := GetProcAddress(DLL, PChar(MethodName));
68
+			if @Method <> nil then begin
69
+				try
70
+					GetMem(Data, 1024);
71
+					I := Method(Data);
72
+					if I > 1 then I := 1;
73
+					writeln(Data);
74
+					ExitCode := I;
75
+				finally
76
+					FreeMem(Data);
77
+				end;
78
+			end
79
+			else begin
80
+				writeln('No Method ('+MethodName+') Found');
81
+			end;
82
+		end
83
+		else begin
84
+			writeln('No DLL ('+Player+'.dll) Found');
85
+		end;
86
+	end
87
+	else begin
88
+		writeln('Usage: GetMediaInfo.exe player function');
89
+		writeln('Example: GetMediaInfo.exe winamp getArtist');
90
+	end;
91
+end.

BIN
src/com/dmdirc/addons/mediasource_windows/files/GetMediaInfo.exe View File


BIN
src/com/dmdirc/addons/mediasource_windows/files/itunes.dll View File


+ 220
- 0
src/com/dmdirc/addons/mediasource_windows/files/itunes.dpr View File

@@ -0,0 +1,220 @@
1
+{*
2
+ * winamp.dpr -> Code for winamp.dll for DMDirc
3
+ * DMDirc - Open Source IRC Client
4
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
5
+ * 
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ * 
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ * 
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ *}
24
+
25
+// References:
26
+// http://www.informit.com/articles/article.aspx?p=130494&seqNum=3
27
+// http://developer.apple.com/sdk/itunescomsdk.html
28
+
29
+library winamp;
30
+uses
31
+	windows, messages, SysUtils, Classes, ComObj, ActiveX;
32
+
33
+var
34
+	oldExitProc: Pointer;
35
+	
36
+function getITunes(): Variant;
37
+begin
38
+	if FindWindow('iTunes', nil) <> 0 then begin
39
+		Result := CreateOLEObject('iTunes.Application');
40
+	end
41
+	else Raise Exception.Create('iTunes is not running');
42
+end;
43
+
44
+// Freepascal sucks.
45
+// In Delphi I could just do V.Foo to call function Foo, but FPC hasn't added
46
+// this functionality yet, so yay for horrible callOLEFunction(V, 'Foo'); to
47
+// do the exact same thing!
48
+function callOLEFunction(V: IDispatch; S: WideString): Variant;
49
+const
50
+	GUID_NULL: TGUID = '{00000000-0000-0000-0000-000000000000}';
51
+	DEFAULT_PARAMS: TDispParams = ();
52
+var
53
+	mres: HRESULT;
54
+	invres: Variant;
55
+	id: Integer;
56
+begin
57
+	mres := V.GetIDsOfNames(GUID_NULL, @S, 1, locale_system_default, @id);
58
+	olecheck(mres);
59
+	mres := V.Invoke(id, GUID_NULL, locale_system_default, DISPATCH_PROPERTYGET, DEFAULT_PARAMS, @invres, nil, nil);
60
+	olecheck(mres);
61
+	Result := invres;
62
+end;
63
+
64
+function getPlayState(data: PChar):integer; stdcall;
65
+var
66
+	V: Variant;
67
+	B: array[0..255] of char;
68
+	state: Integer;
69
+begin
70
+  result := 1;
71
+	try
72
+		V := getITunes();
73
+		state := callOLEFunction(V, 'PlayerState');
74
+		if state = 0 then begin
75
+			try
76
+				callOLEFunction(V, 'PlayerPosition');
77
+				B := 'Paused';
78
+			except
79
+				B := 'Stopped';
80
+			end
81
+		end
82
+		else if state = 1 then B := 'Playing';
83
+		result := 0;
84
+	except
85
+		on E : Exception do B:= 'Unable to find iTunes Application ('+E.ClassName+'/'+E.Message+')';
86
+	end;
87
+	StrCopy(data,B);
88
+end;
89
+
90
+function getArtist(data: PChar):integer; stdcall;
91
+var
92
+	V: Variant;
93
+	B: array[0..255] of char;
94
+begin
95
+  result := 1;
96
+	try
97
+		V := getITunes();
98
+		V := callOLEFunction(V, 'CurrentTrack');
99
+		B := String(callOLEFunction(V, 'Artist'));
100
+		result := 0;
101
+	except
102
+		on E : Exception do B:= 'Unable to find iTunes Application ('+E.ClassName+'/'+E.Message+')';
103
+	end;
104
+	StrCopy(data,B);
105
+end;
106
+
107
+function getTitle(data: PChar):integer; stdcall;
108
+var
109
+	V: Variant;
110
+	B: array[0..255] of char;
111
+	kind: Integer;
112
+begin
113
+  result := 1;
114
+	try
115
+		V := getITunes();
116
+		V := callOLEFunction(V, 'CurrentTrack');
117
+		kind := callOLEFunction(V, 'Kind');
118
+		if kind = 3 then begin
119
+			V := getITunes();
120
+			B := String(callOLEFunction(V, 'CurrentStreamTitle'));
121
+		end
122
+		else begin
123
+			B := String(callOLEFunction(V, 'Name'));
124
+		end;
125
+		result := 0;
126
+	except
127
+		on E : Exception do B:= 'Unable to find iTunes Application ('+E.ClassName+'/'+E.Message+')';
128
+	end;
129
+	StrCopy(data,B);
130
+end;
131
+
132
+function getAlbum(data: PChar):integer; stdcall;
133
+var
134
+	V: Variant;
135
+	B: array[0..255] of char;
136
+begin
137
+  result := 1;
138
+	try
139
+		V := getITunes();
140
+		V := callOLEFunction(V, 'CurrentTrack');
141
+		B := String(callOLEFunction(V, 'Album'));
142
+		result := 0;
143
+	except
144
+		on E : Exception do B:= 'Unable to find iTunes Application ('+E.ClassName+'/'+E.Message+')';
145
+	end;
146
+	StrCopy(data,B);
147
+end;
148
+
149
+function getLength(data: PChar):integer; stdcall;
150
+var
151
+	V: Variant;
152
+	B: array[0..255] of char;
153
+begin
154
+  result := 1;
155
+	try
156
+		V := getITunes();
157
+		V := callOLEFunction(V, 'CurrentTrack');
158
+		B := String(callOLEFunction(V, 'Duration'));
159
+		result := 0;
160
+	except
161
+		on E : Exception do B:= 'Unable to find iTunes Application ('+E.ClassName+'/'+E.Message+')';
162
+	end;
163
+	StrCopy(data,B);
164
+end;
165
+
166
+function getTime(data: PChar):integer; stdcall;
167
+var
168
+	V: Variant;
169
+	B: array[0..255] of char;
170
+begin
171
+  result := 1;
172
+	try
173
+		V := getITunes();
174
+		B := String(callOLEFunction(V, 'PlayerPosition'));
175
+		result := 0;
176
+	except
177
+		on E : Exception do B:= 'Unable to find iTunes Application ('+E.ClassName+'/'+E.Message+')';
178
+	end;
179
+	StrCopy(data,B);
180
+end;
181
+
182
+function getFormat(data: PChar):integer; stdcall;
183
+var
184
+	B: array[0..255] of char;
185
+begin
186
+	Result := 0;
187
+	B := 'Unknown';
188
+	StrCopy(data,B);
189
+end;
190
+
191
+function getBitrate(data: PChar):integer; stdcall;
192
+var
193
+	V: Variant;
194
+	B: array[0..255] of char;
195
+begin
196
+  result := 1;
197
+	try
198
+		V := getITunes();
199
+		V := callOLEFunction(V, 'CurrentTrack');
200
+		B := String(callOLEFunction(V, 'BitRate'));
201
+		result := 0;
202
+	except
203
+		on E : Exception do B:= 'Unable to find iTunes Application ('+E.ClassName+'/'+E.Message+')';
204
+	end;
205
+	StrCopy(data,B);
206
+end;
207
+
208
+exports getPlayState, getArtist, getTitle, getAlbum, getLength, getTime, getFormat, getBitrate;
209
+
210
+procedure myExitProc;
211
+begin
212
+	CoUnInitialize();
213
+	ExitProc := oldExitProc;
214
+end; 
215
+
216
+begin
217
+	CoInitialize(nil);
218
+	oldExitProc := ExitProc;
219
+	ExitProc := @myExitProc;
220
+end.

+ 22
- 0
src/com/dmdirc/addons/mediasource_windows/files/prePackage.sh View File

@@ -0,0 +1,22 @@
1
+#!/bin/sh
2
+# Check if there have been any changes to these files from the svn version
3
+CHANGES=`svn status | grep -v ?`
4
+
5
+compile() {
6
+	# Is there any changes to this file?
7
+	THISCHANGES=`echo ${CHANGES} | grep ${1}`
8
+	
9
+	# Compile if the output doesn't exist, or the local version differs from SVN
10
+	if [ ! -e ${2} -o "" != "${THISCHANGES}" ]; then
11
+		echo "	Compiling: ${1} -> ${2}"
12
+		fpc -Sd -Twin32 ${1} >/dev/null 2>&1
13
+		if [ $? -ne 0 ]; then
14
+			echo "		Failed"
15
+		fi;
16
+		rm -Rf *.o
17
+	fi;
18
+}
19
+
20
+compile "itunes.dpr" "itunes.dll"
21
+compile "winamp.dpr" "winamp.dll"
22
+compile "GetMediaInfo.dpr" "GetMediaInfo.exe"

BIN
src/com/dmdirc/addons/mediasource_windows/files/winamp.dll View File


+ 222
- 0
src/com/dmdirc/addons/mediasource_windows/files/winamp.dpr View File

@@ -0,0 +1,222 @@
1
+{*
2
+ * winamp.dpr -> Code for winamp.dll for DMDirc
3
+ * DMDirc - Open Source IRC Client
4
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
5
+ * 
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ * 
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ * 
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ *}
24
+
25
+// References:
26
+// http://krizzprograming.blogspot.com/2007/08/controlling-winamp-programmatically.html
27
+// http://forums.winamp.com/showthread.php?threadid=85389
28
+
29
+library winamp;
30
+uses
31
+  windows, messages, SysUtils, Classes;
32
+  
33
+function VirtualFreeEx(hProcess:HANDLE; lpAddress:LPVOID; dwSize:DWORD ; dwFreeType : DWORD):WINBOOL; external 'kernel32' name 'VirtualFreeEx';
34
+function VirtualAllocEx(hProcess:HANDLE; lpAddress:LPVOID; dwSize:SIZE_T ; flAllocationType : DWORD;  flProtect : DWORD):POINTER; external 'kernel32' name 'VirtualAllocEx';
35
+
36
+type
37
+	extendedFileInfoStruct = packed record
38
+		filename: pchar;
39
+		metadata: pchar;
40
+		ret: pchar;
41
+		retlen: integer;
42
+	end;
43
+
44
+
45
+const
46
+	WM_WINAMP = WM_USER;
47
+
48
+function getPlayState(data: PChar):integer; stdcall;
49
+var
50
+	hand: THandle;
51
+	res: LongInt;
52
+	B: array[0..255] of char;
53
+begin
54
+	result := 1;
55
+	B := 'Error finding window';
56
+	hand := FindWindow('Winamp v1.x', nil);
57
+	if hand <> 0 then begin
58
+		B := 'Error getting data';
59
+		result := 0;
60
+		res := SendMessage(hand, WM_WINAMP, 0, 104);
61
+		if res = 1 then B := 'Playing'
62
+		else if res = 3 then B := 'Paused'
63
+		else B := 'Stopped';
64
+		StrCopy(data,B);
65
+	end;
66
+end;
67
+
68
+function getArtistTitle(data: PChar):integer; stdcall;
69
+var
70
+	hand: THandle;
71
+	B: array[0..255] of char;
72
+	tempHand: THandle;
73
+	memLoc: LongInt;
74
+	temp: LongWord;
75
+begin
76
+	result := 1;
77
+	B := 'Error finding window';
78
+	hand := FindWindow('Winamp v1.x', nil);
79
+	if hand <> 0 then begin
80
+		B := 'Error getting data';
81
+		result := 0;
82
+		memLoc := SendMessage(hand, WM_WINAMP, 0, 125); // Get number of current track
83
+		memLoc := SendMessage(hand, WM_WINAMP, memLoc, 212); // And now its name
84
+		GetWindowThreadProcessId(hand, @tempHand);
85
+		hand := OpenProcess(PROCESS_ALL_ACCESS, False, tempHand);
86
+		ReadProcessMemory(hand, Pointer(memLoc), @B, sizeof(B)-1, temp);
87
+		CloseHandle(hand);
88
+		StrCopy(data,B);
89
+	end;
90
+end;
91
+
92
+function getMetaData(metadata: PChar): PChar;
93
+var
94
+	hand: THandle;
95
+	readhand: THandle;
96
+	B: array[0..255] of char;
97
+	C: array[0..255] of char;
98
+	tempHand: THandle;
99
+	memPtr: Pointer;
100
+	memLoc: LongInt;
101
+	temp: LongWord;
102
+	extinfo: extendedFileInfoStruct;
103
+begin
104
+	B := '';
105
+	C := '';
106
+	Result := '';
107
+	hand := FindWindow('Winamp v1.x', nil);
108
+	if hand <> 0 then begin
109
+		memLoc := SendMessage(hand, WM_WINAMP, 0, 125); // Get number of current track
110
+		memLoc := SendMessage(hand, WM_WINAMP, memLoc, 211); // And now its filename
111
+		GetWindowThreadProcessId(hand, @tempHand);
112
+		
113
+		readhand := OpenProcess(PROCESS_ALL_ACCESS, False, tempHand);
114
+		ReadProcessMemory(readhand, Pointer(memLoc), @C, sizeof(C)-1, temp);
115
+		
116
+		extinfo.filename := C;
117
+		extinfo.metadata := metadata;
118
+		extinfo.ret := B;
119
+		extinfo.retlen := sizeof(B)-1;
120
+
121
+		// This doesn't actually work yet :/
122
+		// memPtr is nil
123
+		memPtr := VirtualAllocEx(tempHand, nil, 2048, MEM_COMMIT or MEM_RESERVE, PAGE_READWRITE);
124
+		if memPtr <> nil then begin
125
+			WriteProcessMemory(readhand, memPtr, (@extinfo), sizeof(extendedFileInfoStruct), temp);
126
+			SendMessage(readhand, WM_WINAMP, LongInt(memPtr), 290);
127
+		end;
128
+		
129
+		StrCopy(Result, B);
130
+		
131
+		if memPtr <> nil then begin
132
+			VirtualFreeEx(readhand, memPtr, 0, MEM_RELEASE);
133
+		end;
134
+		CloseHandle(readhand);
135
+	end;
136
+end;
137
+
138
+function getArtist(data: PChar):integer; stdcall;
139
+var
140
+	B: PChar;
141
+begin
142
+	B := getMetaData('artist');
143
+	StrCopy(data,B);
144
+	if (B <> '') then Result := 0 else Result := 1;
145
+end;
146
+
147
+function getTitle(data: PChar):integer; stdcall;
148
+var
149
+	B: PChar;
150
+begin
151
+	B := getMetaData('title');
152
+	StrCopy(data,B);
153
+	if (B <> '') then Result := 0 else Result := 1;
154
+end;
155
+
156
+function getAlbum(data: PChar):integer; stdcall;
157
+var
158
+	B: PChar;
159
+begin
160
+	B := getMetaData('album');
161
+	StrCopy(data,B);
162
+	if (B <> '') then Result := 0 else Result := 1;
163
+end;
164
+
165
+function getLength(data: PChar):integer; stdcall;
166
+var
167
+	hand: THandle;
168
+	res: LongInt;
169
+begin
170
+	result := 1;
171
+	StrCopy(data, 'Error finding window');
172
+	hand := FindWindow('Winamp v1.x', nil);
173
+	if hand <> 0 then begin
174
+		result := 0;
175
+		res := SendMessage(hand, WM_WINAMP, 1, 105);
176
+		StrCopy(data, PChar(inttostr(res)));
177
+	end;
178
+end;
179
+
180
+function getTime(data: PChar):integer; stdcall;
181
+var
182
+	hand: THandle;
183
+	res: LongInt;
184
+begin
185
+	result := 1;
186
+	StrCopy(data, 'Error finding window');
187
+	hand := FindWindow('Winamp v1.x', nil);
188
+	if hand <> 0 then begin
189
+		result := 0;
190
+		res := SendMessage(hand, WM_WINAMP, 0, 105);
191
+		StrCopy(data, PChar(inttostr(res div 1000)));
192
+	end;
193
+end;
194
+
195
+function getFormat(data: PChar):integer; stdcall;
196
+var
197
+	B: array[0..255] of char;
198
+begin
199
+	Result := 0;
200
+	B := 'Unknown';
201
+	StrCopy(data,B);
202
+end;
203
+
204
+function getBitrate(data: PChar):integer; stdcall;
205
+var
206
+	hand: THandle;
207
+	res: LongInt;
208
+begin
209
+	result := 1;
210
+	StrCopy(data, 'Error finding window');
211
+	hand := FindWindow('Winamp v1.x', nil);
212
+	if hand <> 0 then begin
213
+		result := 0;
214
+		res := SendMessage(hand, WM_WINAMP, 1, 126);
215
+		StrCopy(data, PChar(inttostr(res)));
216
+	end;
217
+end;
218
+
219
+exports getPlayState, getArtistTitle, getArtist, getTitle, getAlbum, getLength, getTime, getFormat, getBitrate;
220
+
221
+begin
222
+end.

+ 34
- 0
src/com/dmdirc/addons/mediasource_windows/plugin.config View File

@@ -0,0 +1,34 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  requires
9
+  updates
10
+  version
11
+
12
+metadata:
13
+  author=Shane Mc Cormack <shane@dmdirc.com>
14
+  mainclass=com.dmdirc.addons.mediasource_windows.WindowsMediaSourcePlugin
15
+  description=Provides windows media sources for the now playing plugin
16
+  name=windowsmediasource
17
+  nicename=Windows Media Sources
18
+
19
+requires:
20
+  os=.*windows.*
21
+
22
+updates:
23
+  id=30
24
+
25
+version:
26
+  friendly=0.2
27
+
28
+provides:
29
+  winamp mediasource
30
+  itunes mediasource
31
+
32
+required-services:
33
+  mediasource manager
34
+#  windows os

+ 81
- 0
src/com/dmdirc/addons/nickcolours/ColourRenderer.java View File

@@ -0,0 +1,81 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.nickcolours;
24
+
25
+import com.dmdirc.ui.messages.ColourManager;
26
+
27
+import java.awt.Color;
28
+import java.awt.Component;
29
+import javax.swing.JTable;
30
+import javax.swing.border.LineBorder;
31
+import javax.swing.table.DefaultTableCellRenderer;
32
+
33
+/**
34
+ * Provides a colour renderer for JTables.
35
+ *
36
+ * @author Chris
37
+ */
38
+public class ColourRenderer extends DefaultTableCellRenderer {
39
+    
40
+    /**
41
+     * A version number for this class. It should be changed whenever the class
42
+     * structure is changed (or anything else that would prevent serialized
43
+     * objects being unserialized with the new class).
44
+     */
45
+    private static final long serialVersionUID = 1;
46
+    
47
+    /**
48
+     * Creates a new instance of ColourRenderer.
49
+     */
50
+    public ColourRenderer() {
51
+        super();
52
+        
53
+        setOpaque(true);
54
+    }
55
+    
56
+    /** {@inheritDoc} */
57
+    @Override
58
+    public Component getTableCellRendererComponent(final JTable table,
59
+            final Object value, final boolean isSelected,
60
+            final boolean hasFocus, final int row, final int column) {
61
+        Color colour = null;
62
+        if (value != null && !((String) value).isEmpty()) {
63
+            colour = ColourManager.parseColour((String) value, null);
64
+        }
65
+        
66
+        setHorizontalAlignment(CENTER);
67
+        
68
+        if (colour == null) {
69
+            setBorder(new LineBorder(Color.GRAY));
70
+            setText("Not Set");
71
+            setBackground(table.getBackground());
72
+        } else {
73
+            setBorder(new LineBorder(Color.BLACK));
74
+            setText("");
75
+            setBackground(colour);
76
+        }
77
+        
78
+        return this;
79
+    }
80
+    
81
+}

+ 183
- 0
src/com/dmdirc/addons/nickcolours/NickColourInputDialog.java View File

@@ -0,0 +1,183 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.nickcolours;
24
+
25
+import com.dmdirc.Main;
26
+import com.dmdirc.addons.ui_swing.MainFrame;
27
+import com.dmdirc.addons.ui_swing.dialogs.StandardDialog;
28
+import com.dmdirc.addons.ui_swing.components.colours.ColourChooser;
29
+
30
+import java.awt.event.ActionEvent;
31
+import java.awt.event.ActionListener;
32
+
33
+import javax.swing.JButton;
34
+import javax.swing.JLabel;
35
+import javax.swing.JTextField;
36
+import javax.swing.WindowConstants;
37
+
38
+import net.miginfocom.swing.MigLayout;
39
+
40
+/**
41
+ * New nick colour input dialog.
42
+ */
43
+public class NickColourInputDialog extends StandardDialog
44
+        implements ActionListener {
45
+    
46
+    /**
47
+     * A version number for this class. It should be changed whenever the class
48
+     * structure is changed (or anything else that would prevent serialized
49
+     * objects being unserialized with the new class).
50
+     */
51
+    private static final long serialVersionUID = 1;
52
+    
53
+    /** Whether or not this is a new entry (as opposed to editing an old one). */
54
+    private boolean isnew;
55
+    /** The row we're editing, if this isn't a new entry. */
56
+    private int row;
57
+    
58
+    /** The NickColourPanel we're reporting to. */
59
+    private final NickColourPanel panel;
60
+    
61
+    /** nickname textfield. */
62
+    private JTextField nickname;
63
+    /** network textfield. */
64
+    private JTextField network;
65
+    /** text colour input. */
66
+    private ColourChooser textColour;
67
+    /** nicklist colour input. */
68
+    private ColourChooser nicklistColour;
69
+    
70
+    /**
71
+     * Creates a new instance of NickColourInputDialog.
72
+     *
73
+     * @param panel The panel that's opening this dialog
74
+     * @param row The row of the table we're editing
75
+     * @param nickname The nickname that's currently set
76
+     * @param network The network that's currently set
77
+     * @param textcolour The text colour that's currently set
78
+     * @param nickcolour The nicklist colour that's currently set
79
+     */
80
+    public NickColourInputDialog(final NickColourPanel panel, final int row,
81
+            final String nickname, final String network,
82
+            final String textcolour, final String nickcolour) {
83
+        super((MainFrame) Main.getUI().getMainWindow(), false);
84
+        
85
+        this.panel = panel;
86
+        this.row = row;
87
+        
88
+        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
89
+        
90
+        initComponents(nickname, network, textcolour, nickcolour);
91
+        initListeners();
92
+        layoutComponents();
93
+        
94
+        setTitle("Nick colour editor");
95
+        display();
96
+    }
97
+    
98
+    /**
99
+     * Creates a new instance of NickColourInputDialog.
100
+     *
101
+     * @param panel The panel that's opening this dialog
102
+     */
103
+    public NickColourInputDialog(final NickColourPanel panel) {
104
+        this(panel, -1, "", "", "", "");
105
+        
106
+        isnew = true;
107
+    }
108
+    
109
+    /**
110
+     * Initialises the components.
111
+     *
112
+     * @param defaultNickname The default value for the nickname text field
113
+     * @param defaultNetwork The default value for the network text field
114
+     * @param defaultTextColour The default value for the text colour option
115
+     * @param defaultNickColour The default value for the nick colour option
116
+     */
117
+    private void initComponents(final String defaultNickname,
118
+            final String defaultNetwork, final String defaultTextColour,
119
+            final String defaultNickColour) {        
120
+        orderButtons(new JButton(), new JButton());
121
+        
122
+        nickname = new JTextField(defaultNickname);
123
+        network = new JTextField(defaultNetwork);
124
+        textColour = new ColourChooser(defaultTextColour, true, true);
125
+        nicklistColour = new ColourChooser(defaultNickColour, true, true);
126
+    }
127
+    
128
+    /** Initialises the listeners. */
129
+    private void initListeners() {
130
+        getOkButton().addActionListener(this);
131
+        getCancelButton().addActionListener(this);
132
+    }
133
+    
134
+    /** Lays out the components. */
135
+    private void layoutComponents() {        
136
+        setLayout(new MigLayout("wrap 2"));
137
+        
138
+        add(new JLabel("Nickname: "));
139
+        add(nickname, "growx");
140
+        
141
+        add(new JLabel("Network: "));
142
+        add(network, "growx");
143
+        
144
+        add(new JLabel("Text colour: "));
145
+        add(textColour, "growx");
146
+        
147
+        add(new JLabel("Nicklist colour: "));
148
+        add(nicklistColour, "growx");
149
+        
150
+        add(getLeftButton(), "right");
151
+        add(getRightButton(), "right");
152
+        
153
+        pack();
154
+    }
155
+    
156
+    /** 
157
+     * {@inheritDoc} 
158
+     * 
159
+     * @param e Action event
160
+     */
161
+    @Override
162
+    public void actionPerformed(final ActionEvent e) {
163
+        if (e.getSource() == getOkButton()) {
164
+            saveSettings();
165
+        }
166
+        dispose();
167
+    }
168
+    
169
+    /** Saves settings. */
170
+    public void saveSettings() {
171
+        final String myNetwork = network.getText().toLowerCase();
172
+        final String myNickname = nickname.getText().toLowerCase();
173
+        final String myTextColour = textColour.getColour();
174
+        final String myNickColour = nicklistColour.getColour();
175
+        
176
+        if (!isnew) {
177
+            panel.removeRow(row);
178
+        }
179
+        
180
+        panel.addRow(myNetwork, myNickname, myTextColour, myNickColour);
181
+    }
182
+    
183
+}

+ 245
- 0
src/com/dmdirc/addons/nickcolours/NickColourPanel.java View File

@@ -0,0 +1,245 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.nickcolours;
24
+
25
+import com.dmdirc.addons.ui_swing.dialogs.prefs.SwingPreferencesDialog;
26
+import com.dmdirc.config.IdentityManager;
27
+import com.dmdirc.config.prefs.PreferencesInterface;
28
+
29
+import java.awt.Color;
30
+import java.awt.event.ActionEvent;
31
+import java.awt.event.ActionListener;
32
+import java.util.ArrayList;
33
+import java.util.List;
34
+import java.util.Vector;
35
+
36
+import javax.swing.JButton;
37
+import javax.swing.JPanel;
38
+import javax.swing.JScrollPane;
39
+import javax.swing.JTable;
40
+import javax.swing.event.ListSelectionEvent;
41
+import javax.swing.event.ListSelectionListener;
42
+import javax.swing.table.DefaultTableModel;
43
+import javax.swing.table.TableCellRenderer;
44
+
45
+import net.miginfocom.swing.MigLayout;
46
+
47
+/**
48
+ * Panel used for the custom nick colour settings component in the plugin's
49
+ * config dialog.
50
+ *
51
+ * @author Chris
52
+ */
53
+public class NickColourPanel extends JPanel implements ActionListener,
54
+        PreferencesInterface, ListSelectionListener {
55
+
56
+    /**
57
+     * A version number for this class. It should be changed whenever the class
58
+     * structure is changed (or anything else that would prevent serialized
59
+     * objects being unserialized with the new class).
60
+     */
61
+    private static final long serialVersionUID = 1;
62
+    /** The table used for displaying the options. */
63
+    private final JTable table;
64
+    /** The plugin we're associated with. */
65
+    private final transient NickColourPlugin plugin;
66
+    /** The table headings. */
67
+    private static final String[] headers = {"Network", "Nickname",
68
+        "Text colour", "Nicklist colour"};
69
+
70
+    /** Edit and delete buttons. */
71
+    private final JButton editButton, deleteButton;
72
+
73
+    /**
74
+     * Creates a new instance of NickColourPanel.
75
+     * 
76
+     * @param plugin The plugin that owns this panel
77
+     */
78
+    public NickColourPanel(final NickColourPlugin plugin) {
79
+        super();
80
+
81
+        this.plugin = plugin;
82
+
83
+        final Object[][] data = plugin.getData();
84
+
85
+        table = new JTable(new DefaultTableModel(data, headers)) {
86
+
87
+            /**
88
+             * A version number for this class. It should be changed whenever the class
89
+             * structure is changed (or anything else that would prevent serialized
90
+             * objects being unserialized with the new class).
91
+             */
92
+            private static final long serialVersionUID = 1;
93
+            /** The colour renderer we're using for colour cells. */
94
+            private final ColourRenderer colourRenderer = new ColourRenderer();
95
+
96
+            /** {@inheritDoc} */
97
+            @Override
98
+            public TableCellRenderer getCellRenderer(final int row,
99
+                    final int column) {
100
+                if (column == 2 || column == 3) {
101
+                    return colourRenderer;
102
+                } else {
103
+                    return super.getCellRenderer(row, column);
104
+                }
105
+            }
106
+
107
+            /** {@inheritDoc} */
108
+            @Override
109
+            public boolean isCellEditable(final int row, final int column) {
110
+                return false;
111
+            }
112
+        };
113
+
114
+        final JScrollPane scrollPane = new JScrollPane(table);
115
+
116
+        table.getSelectionModel().addListSelectionListener(this);
117
+        table.setFillsViewportHeight(true);
118
+        table.setDefaultRenderer(Color.class, new ColourRenderer());
119
+
120
+        setLayout(new MigLayout("ins 0, fill, h " +
121
+                SwingPreferencesDialog.CLIENT_HEIGHT));
122
+        add(scrollPane, "grow, wrap, spanx, hmax 100%");
123
+
124
+        final JButton addButton = new JButton("Add");
125
+        addButton.addActionListener(this);
126
+        add(addButton, "sg button, growx, pushx");
127
+
128
+        editButton = new JButton("Edit");
129
+        editButton.addActionListener(this);
130
+        add(editButton, "sg button, growx, pushx");
131
+
132
+        deleteButton = new JButton("Delete");
133
+        deleteButton.addActionListener(this);
134
+        add(deleteButton, "sg button, growx, pushx");
135
+
136
+        editButton.setEnabled(false);
137
+        deleteButton.setEnabled(false);
138
+    }
139
+
140
+    /** 
141
+     * {@inheritDoc}
142
+     * 
143
+     * @param e Action event
144
+     */
145
+    @Override
146
+    public void actionPerformed(final ActionEvent e) {
147
+        final DefaultTableModel model = ((DefaultTableModel) table.getModel());
148
+
149
+        if (e.getActionCommand().equals("Add")) {
150
+            new NickColourInputDialog(this);
151
+        } else if (e.getActionCommand().equals("Edit")) {
152
+            final int row = table.getSelectedRow();
153
+
154
+            final String network = (String) model.getValueAt(row, 0);
155
+            final String nickname = (String) model.getValueAt(row, 1);
156
+
157
+            String textcolour = (String) model.getValueAt(row, 2);
158
+            String nickcolour = (String) model.getValueAt(row, 3);
159
+
160
+            if (textcolour == null) {
161
+                textcolour = "";
162
+            }
163
+
164
+            if (nickcolour == null) {
165
+                nickcolour = "";
166
+            }
167
+
168
+            new NickColourInputDialog(this, row, nickname, network, textcolour,
169
+                    nickcolour);
170
+        } else if (e.getActionCommand().equals("Delete")) {
171
+            final int row = table.getSelectedRow();
172
+
173
+            if (row > -1) {
174
+                model.removeRow(row);
175
+            }
176
+        }
177
+    }
178
+
179
+    /**
180
+     * Removes a row from the table.
181
+     * 
182
+     * @param row The row to be removed
183
+     */
184
+    void removeRow(final int row) {
185
+        ((DefaultTableModel) table.getModel()).removeRow(row);
186
+    }
187
+
188
+    /**
189
+     * Adds a row to the table.
190
+     * 
191
+     * @param network The network setting
192
+     * @param nickname The nickname setting
193
+     * @param textcolour The textpane colour setting
194
+     * @param nickcolour The nick list colour setting
195
+     */
196
+    void addRow(final String network, final String nickname,
197
+            final String textcolour, final String nickcolour) {
198
+        final DefaultTableModel model = ((DefaultTableModel) table.getModel());
199
+        model.addRow(new Object[]{network, nickname, textcolour, nickcolour});
200
+    }
201
+
202
+    /**
203
+     * Retrieves the current data in use by this panel.
204
+     * 
205
+     * @return This panel's current data.
206
+     */
207
+    List<Object[]> getData() {
208
+        final List<Object[]> res = new ArrayList<Object[]>();
209
+        final DefaultTableModel model = ((DefaultTableModel) table.getModel());
210
+
211
+        for (Object row : model.getDataVector()) {
212
+            final Vector vrow = (Vector) row;
213
+
214
+            res.add(new Object[]{vrow.elementAt(0), vrow.elementAt(1), vrow.
215
+                        elementAt(2), vrow.elementAt(3)});
216
+        }
217
+
218
+        return res;
219
+    }
220
+
221
+    /** {@inheritDoc} */
222
+    @Override
223
+    public void save() {
224
+        // Remove all old config entries
225
+        for (Object[] parts : plugin.getData()) {
226
+            IdentityManager.getConfigIdentity().unsetOption(
227
+                    plugin.getDomain(), "color:" + parts[0] + ":" + parts[1]);
228
+        }
229
+
230
+        // And write the new ones
231
+        for (Object[] row : getData()) {
232
+            IdentityManager.getConfigIdentity().setOption(plugin.getDomain(),
233
+                    "color:" + row[0] + ":" + row[1], row[2] + ":" + row[3]);
234
+        }
235
+    }
236
+
237
+    /** {@inheritDoc} */
238
+    @Override
239
+    public void valueChanged(ListSelectionEvent e) {
240
+        boolean enable = table.getSelectedRow() > -1 && table.getModel().getRowCount() > 0;
241
+
242
+        editButton.setEnabled(enable);
243
+        deleteButton.setEnabled(enable);
244
+    }
245
+}

+ 269
- 0
src/com/dmdirc/addons/nickcolours/NickColourPlugin.java View File

@@ -0,0 +1,269 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.nickcolours;
24
+
25
+import com.dmdirc.Channel;
26
+import com.dmdirc.ChannelClientProperty;
27
+import com.dmdirc.actions.ActionManager;
28
+import com.dmdirc.actions.interfaces.ActionType;
29
+import com.dmdirc.actions.CoreActionType;
30
+import com.dmdirc.config.IdentityManager;
31
+import com.dmdirc.config.prefs.PreferencesCategory;
32
+import com.dmdirc.config.prefs.PreferencesManager;
33
+import com.dmdirc.config.prefs.PreferencesSetting;
34
+import com.dmdirc.config.prefs.PreferencesType;
35
+import com.dmdirc.interfaces.ActionListener;
36
+import com.dmdirc.parser.interfaces.ChannelClientInfo;
37
+import com.dmdirc.parser.interfaces.ChannelInfo;
38
+import com.dmdirc.parser.interfaces.ClientInfo;
39
+import com.dmdirc.plugins.Plugin;
40
+import com.dmdirc.ui.messages.ColourManager;
41
+
42
+import java.awt.Color;
43
+import java.util.ArrayList;
44
+import java.util.List;
45
+import java.util.Map;
46
+
47
+/**
48
+ * Provides various features related to nickname colouring.
49
+ *
50
+ * @author chris
51
+ */
52
+public final class NickColourPlugin extends Plugin implements ActionListener {
53
+    
54
+    /** "Random" colours to use to colour nicknames. */
55
+    private String[] randColours = new String[] {
56
+        "E90E7F", "8E55E9", "B30E0E", "18B33C",
57
+        "58ADB3", "9E54B3", "B39875", "3176B3",
58
+    };
59
+    
60
+    /** Creates a new instance of NickColourPlugin. */
61
+    public NickColourPlugin() {
62
+        super();
63
+    }
64
+    
65
+    /** {@inheritDoc} */
66
+    @Override
67
+    public void processEvent(final ActionType type, final StringBuffer format,
68
+            final Object... arguments) {
69
+        if (type.equals(CoreActionType.CHANNEL_GOTNAMES)) {
70
+            final ChannelInfo chanInfo = ((Channel) arguments[0]).getChannelInfo();
71
+            final String network = ((Channel) arguments[0]).getServer().getNetwork();
72
+            
73
+            for (ChannelClientInfo client : chanInfo.getChannelClients()) {
74
+                colourClient(network, client);
75
+            }
76
+        } else if (type.equals(CoreActionType.CHANNEL_JOIN)) {
77
+            final String network = ((Channel) arguments[0]).getServer().getNetwork();
78
+            
79
+            colourClient(network, (ChannelClientInfo) arguments[1]);
80
+        }
81
+    }
82
+    
83
+    /**
84
+     * Colours the specified client according to the user's config.
85
+     *
86
+     * @param network The network to use for the colouring
87
+     * @param client The client to be coloured
88
+     */
89
+    private void colourClient(final String network, final ChannelClientInfo client) {
90
+        final Map map = client.getMap();
91
+        final ClientInfo myself = client.getClient().getParser().getLocalClient();
92
+        final String nickOption1 = "color:"
93
+                + client.getClient().getParser().getStringConverter().toLowerCase(network + ":" + client.getClient().getNickname());
94
+        final String nickOption2 = "color:"
95
+                + client.getClient().getParser().getStringConverter().toLowerCase("*:" + client.getClient().getNickname());
96
+        
97
+        if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "useowncolour")
98
+                && client.getClient().equals(myself)) {
99
+            final Color color = ColourManager.parseColour(
100
+                    IdentityManager.getGlobalConfig().getOption(getDomain(), "owncolour"));
101
+            putColour(map, color, color);
102
+        }  else if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "userandomcolour")) {
103
+            putColour(map, getColour(client.getClient().getNickname()), getColour(client.getClient().getNickname()));
104
+        }
105
+        
106
+        String[] parts = null;
107
+                
108
+        if (IdentityManager.getGlobalConfig().hasOptionString(getDomain(), nickOption1)) {
109
+            parts = getParts(nickOption1);
110
+        } else if (IdentityManager.getGlobalConfig().hasOptionString(getDomain(), nickOption2)) {
111
+            parts = getParts(nickOption2);
112
+        }
113
+        
114
+        if (parts != null) {
115
+            Color textColor = null;
116
+            Color nickColor = null;
117
+            
118
+            if (parts[0] != null) {
119
+                textColor = ColourManager.parseColour(parts[0], null);
120
+            }
121
+            if (parts[1] != null) {
122
+                nickColor = ColourManager.parseColour(parts[1], null);
123
+            }
124
+            
125
+            putColour(map, textColor, nickColor);
126
+        }
127
+    }
128
+    
129
+    /**
130
+     * Puts the specified colour into the given map. The keys are determined
131
+     * by config settings.
132
+     *
133
+     * @param map The map to use
134
+     * @param textColour Text colour to be inserted
135
+     * @param nickColour Nick colour to be inserted
136
+     */
137
+    @SuppressWarnings("unchecked")
138
+    private void putColour(final Map map, final Color textColour, final Color nickColour) {
139
+        if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "settext")
140
+                && textColour != null) {
141
+            map.put(ChannelClientProperty.TEXT_FOREGROUND, textColour);
142
+        }
143
+        
144
+        if (IdentityManager.getGlobalConfig().getOptionBool(getDomain(), "setnicklist")
145
+                && nickColour != null) {
146
+            map.put(ChannelClientProperty.NICKLIST_FOREGROUND, nickColour);
147
+        }
148
+    }
149
+    
150
+    /**
151
+     * Retrieves a pseudo-random colour for the specified nickname.
152
+     *
153
+     * @param nick The nickname of the client whose colour we're determining
154
+     * @return Colour of the specified nickname
155
+     */
156
+    private Color getColour(final String nick) {
157
+        int count = 0;
158
+        
159
+        for (int i = 0; i < nick.length(); i++) {
160
+            count += nick.charAt(i);
161
+        }
162
+        
163
+        count = count % randColours.length;
164
+        
165
+        return ColourManager.parseColour(randColours[count]);
166
+    }
167
+    
168
+    /**
169
+     * Reads the nick colour data from the config.
170
+     *
171
+     * @return A multi-dimensional array of nick colour info.
172
+     */
173
+    public Object[][] getData() {
174
+        final List<Object[]> data = new ArrayList<Object[]>();
175
+        
176
+        for (String key : IdentityManager.getGlobalConfig().getOptions(getDomain()).keySet()) {
177
+            if (key.startsWith("color:")) {
178
+                final String network = key.substring(6, key.indexOf(':', 6));
179
+                final String user = key.substring(1 + key.indexOf(':', 6));
180
+                final String[] parts = getParts(key);
181
+                
182
+                
183
+                data.add(new Object[]{network, user, parts[0], parts[1]});
184
+            }
185
+        }
186
+        
187
+        final Object[][] res = new Object[data.size()][4];
188
+        
189
+        int i = 0;
190
+        for (Object[] row : data) {
191
+            res[i] = row;
192
+            
193
+            i++;
194
+        }
195
+        
196
+        return res;
197
+    }
198
+    
199
+    /**
200
+     * Retrieves the config option with the specified key, and returns an
201
+     * array of the colours that should be used for it.
202
+     * 
203
+     * @param key The config key to look up
204
+     * @return The colours specified by the given key
205
+     */
206
+    private String[] getParts(final String key) {
207
+        String[] parts = IdentityManager.getGlobalConfig().getOption(getDomain(), key).split(":");
208
+        
209
+        if (parts.length == 0) {
210
+            parts = new String[]{null, null};
211
+        } else if (parts.length == 1) {
212
+            parts = new String[]{parts[0], null};
213
+        }
214
+        
215
+        return parts;
216
+    }
217
+    
218
+    /** {@inheritDoc} */
219
+    @Override
220
+    public void onLoad() {
221
+        if (IdentityManager.getGlobalConfig().hasOptionString(getDomain(), "randomcolours")) {
222
+            randColours = IdentityManager.getGlobalConfig().getOptionList(getDomain(), "randomcolours").toArray(new String[0]);
223
+        }
224
+        
225
+        ActionManager.addListener(this, CoreActionType.CHANNEL_GOTNAMES,
226
+                CoreActionType.CHANNEL_JOIN);
227
+    }
228
+    
229
+    /** {@inheritDoc} */
230
+    @Override
231
+    public void onUnload() {
232
+        ActionManager.removeListener(this);
233
+    }
234
+    
235
+    /** {@inheritDoc} */
236
+    @Override
237
+    public void showConfig(final PreferencesManager manager) {
238
+        final PreferencesCategory general = new PreferencesCategory("Nick Colours",
239
+                "General configuration for NickColour plugin.");
240
+        final PreferencesCategory colours = new PreferencesCategory("Colours",
241
+                "Set colours for specific nicknames.", new NickColourPanel(this));
242
+        
243
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
244
+                "ui", "shownickcoloursintext", "Show colours in text" +
245
+                "area", "Colour nicknames in main text area?"));
246
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
247
+                "ui", "shownickcoloursinnicklist", "Show colours in" +
248
+                " nick list", "Colour nicknames in channel nick lists?"));
249
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
250
+                getDomain(), "settext", "Set colours in textarea",
251
+                "Should the plugin set the textarea colour of nicks?"));
252
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
253
+                getDomain(), "setnicklist", "Set colours in nick list",
254
+                "Should the plugin set the nick list colour of nicks?"));
255
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
256
+                getDomain(), "userandomcolour", "Use random colour",
257
+                "Use a pseudo-random colour for each person?"));
258
+        general.addSetting(new PreferencesSetting(PreferencesType.BOOLEAN,
259
+                getDomain(), "useowncolour", "Use colour for own nick",
260
+                "Always use the same colour for our own nickname?"));
261
+        general.addSetting(new PreferencesSetting(PreferencesType.COLOUR,
262
+                getDomain(), "owncolour", "Colour to use for own nick",
263
+                "Colour used for our own nickname, if above setting is enabled."));        
264
+
265
+        general.addSubCategory(colours);
266
+        manager.getCategory("Plugins").addSubCategory(general);
267
+    }
268
+    
269
+}

+ 26
- 0
src/com/dmdirc/addons/nickcolours/package-info.java View File

@@ -0,0 +1,26 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+/**
24
+ * Plugin to colourise nicks.
25
+ */
26
+package com.dmdirc.addons.nickcolours;

+ 40
- 0
src/com/dmdirc/addons/nickcolours/plugin.config View File

@@ -0,0 +1,40 @@
1
+# This is a DMDirc configuration file.
2
+
3
+# This section indicates which sections below take key/value
4
+# pairs, rather than a simple list. It should be placed above
5
+# any sections that take key/values.
6
+keysections:
7
+  metadata
8
+  updates
9
+  requires
10
+  defaults
11
+  version
12
+
13
+metadata:
14
+  author=Chris <chris@dmdirc.com>
15
+  mainclass=com.dmdirc.addons.nickcolours.NickColourPlugin
16
+  description=Provides various nick colouring tools
17
+  name=nickcolour
18
+  nicename=Nick Colour Plugin
19
+
20
+updates:
21
+  id=12
22
+
23
+requires:
24
+  parent=ui_swing
25
+
26
+version:
27
+  friendly=0.7
28
+
29
+persistent:
30
+
31
+provides:
32
+
33
+required-services:
34
+
35
+defaults:
36
+  useowncolour=false
37
+  owncolour=ff0000
38
+  userandomcolour=false
39
+  settext=false
40
+  setnicklist=false

+ 309
- 0
src/com/dmdirc/addons/nowplaying/ConfigPanel.java View File

@@ -0,0 +1,309 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.nowplaying;
24
+
25
+import com.dmdirc.config.IdentityManager;
26
+import com.dmdirc.config.prefs.PreferencesInterface;
27
+import com.dmdirc.addons.ui_swing.components.text.TextLabel;
28
+import com.dmdirc.addons.ui_swing.components.reorderablelist.ReorderableJList;
29
+
30
+import com.dmdirc.logger.ErrorLevel;
31
+import com.dmdirc.logger.Logger;
32
+import java.awt.event.KeyEvent;
33
+import java.awt.event.KeyListener;
34
+import java.util.Arrays;
35
+import java.util.Enumeration;
36
+import java.util.LinkedList;
37
+import java.util.List;
38
+import java.util.Timer;
39
+import java.util.TimerTask;
40
+
41
+import javax.swing.BorderFactory;
42
+import javax.swing.JLabel;
43
+import javax.swing.JPanel;
44
+import javax.swing.JScrollPane;
45
+import javax.swing.JTextField;
46
+import javax.swing.SwingUtilities;
47
+import javax.swing.UIManager;
48
+
49
+import net.miginfocom.swing.MigLayout;
50
+
51
+/**
52
+ * Now playing plugin config panel.
53
+ */
54
+public class ConfigPanel extends JPanel implements PreferencesInterface,
55
+        KeyListener {
56
+
57
+    /**
58
+     * A version number for this class. It should be changed whenever the class
59
+     * structure is changed (or anything else that would prevent serialized
60
+     * objects being unserialized with the new class).
61
+     */
62
+    private static final long serialVersionUID = 1;
63
+    /** Media source order list. */
64
+    private ReorderableJList list;
65
+    /** Media sources. */
66
+    private final List<String> sources;
67
+    /** The plugin that owns this panel. */
68
+    private final NowPlayingPlugin plugin;
69
+    /** Text field for our setting. */
70
+    private JTextField textfield;
71
+    /** Panel that the preview is in. */
72
+    private JPanel previewPanel;
73
+    /** Label for previews. */
74
+    private TextLabel preview;
75
+    /** Update timer. */
76
+    private Timer updateTimer;
77
+
78
+    /**
79
+     * Creates a new instance of ConfigPanel.
80
+     *
81
+     * @param plugin The plugin that owns this panel
82
+     * @param sources A list of sources to be used in the panel
83
+     */
84
+    public ConfigPanel(final NowPlayingPlugin plugin, final List<String> sources) {
85
+        super();
86
+
87
+        if (sources == null) {
88
+            this.sources = new LinkedList<String>();
89
+        } else {
90
+            this.sources = new LinkedList<String>(sources);
91
+        }
92
+        this.plugin = plugin;
93
+
94
+        initComponents();
95
+    }
96
+
97
+    /**
98
+     * Initialises the components.
99
+     */
100
+    private void initComponents() {
101
+        list = new ReorderableJList();
102
+
103
+        for (String source : sources) {
104
+            list.getModel().addElement(source);
105
+        }
106
+
107
+        textfield = new JTextField(IdentityManager.getGlobalConfig().getOption(
108
+                plugin.getDomain(), "format"));
109
+        textfield.addKeyListener(this);
110
+        preview = new TextLabel("Preview:\n");
111
+
112
+        setLayout(new MigLayout("fillx, ins 0"));
113
+
114
+        JPanel panel = new JPanel();
115
+
116
+        panel.setBorder(BorderFactory.createTitledBorder(UIManager.getBorder(
117
+                "TitledBorder.border"), "Source order"));
118
+        panel.setLayout(new MigLayout("fillx, ins 5"));
119
+
120
+        panel.add(new JLabel("Drag and drop items to reorder"), "wrap");
121
+        panel.add(new JScrollPane(list), "growx");
122
+
123
+        add(panel, "growx, wrap");
124
+
125
+        panel = new JPanel();
126
+
127
+        panel.setBorder(BorderFactory.createTitledBorder(UIManager.getBorder(
128
+                "TitledBorder.border"), "Output format"));
129
+        panel.setLayout(new MigLayout("fillx, ins 5"));
130
+
131
+        panel.add(textfield, "span, growx, wrap");
132
+        panel.add(preview, "span, grow, wrap, gaptop 10");
133
+        add(panel, "growx, wrap");
134
+
135
+        previewPanel = panel;
136
+
137
+        add(new NowPlayingSubsitutionPanel(Arrays.asList(new String[]{"app",
138
+                    "title", "artist", "album", "bitrate", "format", "length",
139
+                    "time",
140
+                    "state"})), "growx");
141
+        schedulePreviewUpdate();
142
+    }
143
+
144
+    /**
145
+     * Updates the preview text.
146
+     */
147
+    private void updatePreview() {
148
+        updateTimer.cancel();
149
+
150
+        MediaSource source = plugin.getBestSource();
151
+
152
+        if (source == null) {
153
+            source = new DummyMediaSource();
154
+        }
155
+
156
+        preview.setText("Preview:\n" + plugin.doSubstitution(textfield.getText(),
157
+                source));
158
+        preview.repaint();
159
+
160
+        SwingUtilities.invokeLater(new Runnable() {
161
+
162
+            @Override
163
+            public void run() {
164
+                previewPanel.revalidate();
165
+                revalidate();
166
+            }
167
+        });
168
+    }
169
+
170
+    /**
171
+     * Retrieves the (new) source order from this config panel.
172
+     *
173
+     * @return An ordered list of sources
174
+     */
175
+    public List<String> getSources() {
176
+        final List<String> newSources = new LinkedList<String>();
177
+
178
+        final Enumeration<?> values = list.getModel().elements();
179
+
180
+        while (values.hasMoreElements()) {
181
+            newSources.add((String) values.nextElement());
182
+        }
183
+
184
+        return newSources;
185
+    }
186
+
187
+    /** {@inheritDoc} */
188
+    @Override
189
+    public void save() {
190
+        plugin.saveSettings(getSources());
191
+        IdentityManager.getConfigIdentity().setOption(plugin.getDomain(),
192
+                "format", textfield.getText());
193
+    }
194
+
195
+    /**
196
+     * {@inheritDoc}
197
+     *
198
+     * @param e Key event action
199
+     */
200
+    @Override
201
+    public void keyTyped(final KeyEvent e) {
202
+        // Do nothing
203
+    }
204
+
205
+    /**
206
+     * {@inheritDoc}
207
+     *
208
+     * @param e Key event action
209
+     */
210
+    @Override
211
+    public void keyPressed(final KeyEvent e) {
212
+        // Do nothing
213
+    }
214
+
215
+    /**
216
+     * {@inheritDoc}
217
+     *
218
+     * @param e Key event action
219
+     */
220
+    @Override
221
+    public void keyReleased(final KeyEvent e) {
222
+        schedulePreviewUpdate();
223
+    }
224
+
225
+    /**
226
+     * Schedules an update to the preview text.
227
+     */
228
+    private void schedulePreviewUpdate() {
229
+        if (updateTimer != null) {
230
+            updateTimer.cancel();
231
+        }
232
+
233
+        updateTimer = new Timer("Nowplaying config timer");
234
+        updateTimer.schedule(new TimerTask() {
235
+
236
+            /** {@inheritDoc} */
237
+            @Override
238
+            public void run() {
239
+                try {
240
+                    updatePreview();
241
+                } catch (Throwable ex) {
242
+                    Logger.appError(ErrorLevel.MEDIUM,
243
+                            "Error when updating nowplaying preview", ex);
244
+                }
245
+            }
246
+        }, 500);
247
+    }
248
+
249
+    /**
250
+     * A dummy media source for use in previews.
251
+     */
252
+    private class DummyMediaSource implements MediaSource {
253
+
254
+        /** {@inheritDoc} */
255
+        @Override
256
+        public MediaSourceState getState() {
257
+            return MediaSourceState.PLAYING;
258
+        }
259
+
260
+        /** {@inheritDoc} */
261
+        @Override
262
+        public String getAppName() {
263
+            return "MyProgram";
264
+        }
265
+
266
+        /** {@inheritDoc} */
267
+        @Override
268
+        public String getArtist() {
269
+            return "The Artist";
270
+        }
271
+
272
+        /** {@inheritDoc} */
273
+        @Override
274
+        public String getTitle() {
275
+            return "Song about nothing";
276
+        }
277
+
278
+        /** {@inheritDoc} */
279
+        @Override
280
+        public String getAlbum() {
281
+            return "Album 45";
282
+        }
283
+
284
+        /** {@inheritDoc} */
285
+        @Override
286
+        public String getLength() {
287
+            return "3:45";
288
+        }
289
+
290
+        /** {@inheritDoc} */
291
+        @Override
292
+        public String getTime() {
293
+            return "1:20";
294
+        }
295
+
296
+        /** {@inheritDoc} */
297
+        @Override
298
+        public String getFormat() {
299
+            return "flac";
300
+        }
301
+
302
+        /** {@inheritDoc} */
303
+        @Override
304
+        public String getBitrate() {
305
+            return "128";
306
+        }
307
+    }
308
+
309
+}

+ 96
- 0
src/com/dmdirc/addons/nowplaying/MediaSource.java View File

@@ -0,0 +1,96 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.nowplaying;
24
+
25
+/**
26
+ * The media source describes one source of "now playing" information
27
+ * (i.e., one method of getting information from one media player).
28
+ * 
29
+ * @author chris
30
+ */
31
+public interface MediaSource {
32
+    /**
33
+     * Get the state of this media source
34
+     *
35
+     * @return State for this media source.
36
+     * @since 0.6.3m1
37
+     */
38
+    MediaSourceState getState();
39
+    
40
+    /**
41
+     * Retrieves the name of the application that this source is for.
42
+     * 
43
+     * @return This source's application name
44
+     */
45
+    String getAppName();
46
+    
47
+    /**
48
+     * Retrieves the artist of the curently loaded track.
49
+     *
50
+     * @return Current track artist
51
+     */
52
+    String getArtist();
53
+    
54
+    /**
55
+     * Retrieves the title of the currently loaded track.
56
+     *
57
+     * @return Current track title
58
+     */
59
+    String getTitle();
60
+    
61
+    /**
62
+     * Retrieves the album of the currently loaded track.
63
+     *
64
+     * @return Current track album
65
+     */
66
+    String getAlbum();
67
+    
68
+    /**
69
+     * Retrieves the length of the currently loaded track ([h:]mm:ss).
70
+     *
71
+     * @return Current track length
72
+     */
73
+    String getLength();
74
+    
75
+    /**
76
+     * Retrieves the time of the currently loaded track ([h:]mm:ss).
77
+     *
78
+     * @return Current track time
79
+     */
80
+    String getTime();
81
+    
82
+    /**
83
+     * Retrives the format of the currently loaded track.
84
+     *
85
+     * @return Current track format
86
+     */
87
+    String getFormat();
88
+    
89
+    /**
90
+     * Retrieves the bitrate of the currently loaded track.
91
+     *
92
+     * @return Current track bitrate
93
+     */
94
+    String getBitrate();
95
+
96
+}

+ 76
- 0
src/com/dmdirc/addons/nowplaying/MediaSourceComparator.java View File

@@ -0,0 +1,76 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.nowplaying;
24
+
25
+import java.io.Serializable;
26
+import java.util.Comparator;
27
+import java.util.List;
28
+
29
+/**
30
+ * Sorts media sources according to an ordered list of their names.
31
+ *
32
+ * @author chris
33
+ */
34
+public class MediaSourceComparator implements Comparator<MediaSource>, Serializable {
35
+    
36
+    /**
37
+     * A version number for this class. It should be changed whenever the class
38
+     * structure is changed (or anything else that would prevent serialized
39
+     * objects being unserialized with the new class).
40
+     */
41
+    private static final long serialVersionUID = 1;
42
+    
43
+    /** The order that the sources should be checked. */
44
+    private final List<String> order;
45
+    
46
+    /**
47
+     * Creates a new instance of MediaSourceComparator.
48
+     * NB: The order list may be altered during comparisons.
49
+     *
50
+     * @param order An ordered list of media source names
51
+     */
52
+    public MediaSourceComparator(final List<String> order) {
53
+        this.order = order;
54
+    }
55
+
56
+    /** {@inheritDoc} */
57
+    @Override
58
+    public int compare(final MediaSource o1, final MediaSource o2) {
59
+        return getPosition(o1) - getPosition(o2);
60
+    }
61
+    
62
+    /**
63
+     * Retrieves the position of the source within the order list.
64
+     * If the source is not present it is appended to the list.
65
+     *
66
+     * @param source The media source to be tested
67
+     */
68
+    private int getPosition(final MediaSource source) {
69
+        if (!order.contains(source.getAppName())) {
70
+            order.add(source.getAppName());
71
+        }
72
+        
73
+        return order.indexOf(source.getAppName());
74
+    }
75
+    
76
+}

+ 42
- 0
src/com/dmdirc/addons/nowplaying/MediaSourceManager.java View File

@@ -0,0 +1,42 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.nowplaying;
24
+
25
+import java.util.List;
26
+
27
+/**
28
+ * The media source manager is a standard interface for an object that controls
29
+ * one or more media sources.
30
+ * 
31
+ * @author chris
32
+ */
33
+public interface MediaSourceManager {
34
+    
35
+    /**
36
+     * Retrieves the sources that this manager manages.
37
+     * 
38
+     * @return A list of sources that this manager manages
39
+     */
40
+    List<MediaSource> getSources();
41
+    
42
+}

+ 63
- 0
src/com/dmdirc/addons/nowplaying/MediaSourceState.java View File

@@ -0,0 +1,63 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.addons.nowplaying;
24
+
25
+/**
26
+ * The state of a media source.
27
+ *
28
+ * @author Shane McCormack
29
+ * @since 0.6.3m1
30
+ */
31
+public enum MediaSourceState {
32
+	/** Media Source is closed. */
33
+	CLOSED ("Closed"),
34
+	/** Media Source is stopped. */
35
+	STOPPED ("Stopped"),
36
+	/** Media Source is paused. */
37
+	PAUSED ("Paused"),
38
+	/** Media Source is playing. */
39
+	PLAYING ("Playing"),
40
+	/** Media Source is giving an unknown state. */
41
+	NOTKNOWN ("Unknown");
42
+	
43
+	/** Nice name for this state. */
44
+	final String niceName;
45
+	
46
+	/**
47
+	 * Create a new MediaSourceState
48
+	 *
49
+	 * @param niceName Nice name for this state.
50
+	 */
51
+	MediaSourceState(final String niceName) {
52
+		this.niceName = niceName;
53
+	}
54
+	
55
+	/**
56
+	 * Get the nice name for this state.
57
+     *
58
+     * @return This state's nice name
59
+	 */
60
+	public String getNiceName() {
61
+		return niceName;
62
+	}
63
+}

+ 0
- 0
src/com/dmdirc/addons/nowplaying/NowPlayingCommand.java View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save