/SparkleLib/SparkleListenerIrc.cs
C# | 219 lines | 146 code | 46 blank | 27 comment | 17 complexity | 49b21b59d3b438dae56d3b43b72c0124 MD5 | raw file
1// SparkleShare, a collaboration and sharing tool. 2// Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com> 3// 4// This program is free software: you can redistribute it and/or modify 5// it under the terms of the GNU General Public License as published by 6// the Free Software Foundation, either version 3 of the License, or 7// (at your option) any later version. 8// 9// This program is distributed in the hope that it will be useful, 10// but WITHOUT ANY WARRANTY; without even the implied warranty of 11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12// GNU General Public License for more details. 13// 14// You should have received a copy of the GNU General Public License 15// along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 18using System; 19using System.Text; 20using System.Threading; 21using System.Security.Cryptography; 22 23using Meebey.SmartIrc4net; 24 25namespace SparkleLib { 26 27 public class SparkleListenerIrc : SparkleListenerBase { 28 29 private Thread thread; 30 private IrcClient client; 31 private string nick; 32 private string announcements_password; 33 private bool allow_passwordless_join; 34 35 36 public SparkleListenerIrc (Uri server, string folder_identifier) : 37 base (server, folder_identifier) 38 { 39 // Try to get a uniqueish nickname 40 this.nick = SHA1 (DateTime.Now.ToString ("ffffff") + "sparkles"); 41 42 // Most irc servers don't allow nicknames starting 43 // with a number, so prefix an alphabetic character 44 this.nick = "s" + this.nick.Substring (0, 7); 45 46 // Optional password to make the channel access more safe 47 this.announcements_password = 48 SparkleConfig.DefaultConfig.GetConfigOption ("announcements_password"); 49 50 // Option to allow access to channel when no password is defined 51 try { 52 string option = SparkleConfig.DefaultConfig.GetConfigOption ("allow_passwordless_join"); 53 this.allow_passwordless_join = (option == null || Convert.ToBoolean (option)); 54 } catch (Exception) { 55 this.allow_passwordless_join = true; 56 } 57 58 base.channels.Add ("#" + folder_identifier); 59 60 this.client = new IrcClient () { 61 PingTimeout = 180, 62 PingInterval = 60 63 }; 64 65 string proxy = Environment.GetEnvironmentVariable ("http_proxy"); 66 Uri proxy_uri = null; 67 if (!String.IsNullOrEmpty (proxy) && 68 Uri.TryCreate (proxy, UriKind.Absolute, out proxy_uri)) { 69 70 if (proxy_uri.Scheme == "http") { 71 this.client.ProxyType = ProxyType.Http; 72 this.client.ProxyHost = proxy_uri.Host; 73 this.client.ProxyPort = proxy_uri.Port; 74 } 75 } 76 77 this.client.OnConnected += delegate { 78 base.is_connecting = false; 79 OnConnected (); 80 }; 81 82 this.client.OnDisconnected += delegate { 83 base.is_connecting = false; 84 OnDisconnected (); 85 }; 86 87 this.client.OnError += delegate { 88 base.is_connecting = false; 89 OnDisconnected (); 90 }; 91 92 this.client.OnChannelMessage += delegate (object o, IrcEventArgs args) { 93 string message = args.Data.Message.Trim (); 94 string folder_id = args.Data.Channel.Substring (1); // remove the starting hash 95 OnAnnouncement (new SparkleAnnouncement (folder_id, message)); 96 }; 97 } 98 99 100 public override bool IsConnected { 101 get { 102 return this.client.IsConnected; 103 } 104 } 105 106 107 // Starts a new thread and listens to the channel 108 public override void Connect () 109 { 110 SparkleHelpers.DebugInfo ("ListenerIrc", "Connecting to " + Server); 111 112 base.is_connecting = true; 113 114 this.thread = new Thread ( 115 new ThreadStart (delegate { 116 try { 117 // Connect, login, and join the channel 118 int port = base.server.Port; 119 120 if (port < 0) 121 port = 6667; 122 123 this.client.Connect (base.server.Host, port); 124 this.client.Login (this.nick, this.nick, 8, this.nick); 125 126 foreach (string channel in base.channels) { 127 SparkleHelpers.DebugInfo ("ListenerIrc", "Joining channel " + channel); 128 129 if (!string.IsNullOrEmpty (this.announcements_password)) { 130 SparkleHelpers.DebugInfo ("ListenerIrc", "Password set to access the channel"); 131 this.client.RfcJoin (channel, this.announcements_password); 132 this.client.RfcMode (channel, "+k " + this.announcements_password); 133 134 } else { 135 if (this.allow_passwordless_join) { 136 SparkleHelpers.DebugInfo ("ListenerIrc", "Accessing unprotected channel, change the setting to not access"); 137 this.client.RfcJoin (channel); 138 139 } else { 140 SparkleHelpers.DebugInfo ("ListenerIrc", "Unprotected channel, change the setting to access"); 141 base.is_connecting = false; 142 OnDisconnected (); 143 throw new ConnectionException ("Unprotected channel, change the setting to access"); 144 } 145 } 146 147 this.client.RfcMode (channel, "+s"); 148 } 149 150 // List to the channel, this blocks the thread 151 this.client.Listen (); 152 153 // Disconnect when we time out 154 this.client.Disconnect (); 155 156 } catch (ConnectionException e) { 157 SparkleHelpers.DebugInfo ("ListenerIrc", "Could not connect to " + Server + ": " + e.Message); 158 } 159 }) 160 ); 161 162 this.thread.Start (); 163 } 164 165 166 public override void AlsoListenTo (string folder_identifier) 167 { 168 string channel = "#" + folder_identifier; 169 if (!base.channels.Contains (channel)) { 170 base.channels.Add (channel); 171 172 if (IsConnected) { 173 SparkleHelpers.DebugInfo ("ListenerIrc", "Joining channel " + channel); 174 if (this.announcements_password != null) { 175 SparkleHelpers.DebugInfo ("ListenerIrc", "Password set to access the channel"); 176 this.client.RfcJoin (channel, this.announcements_password); 177 this.client.RfcMode (channel, "+k " + this.announcements_password); 178 } else { 179 if (allow_passwordless_join) { 180 SparkleHelpers.DebugInfo ("ListenerIrc", "Accessing a dangerous channel change the setting to not access"); 181 this.client.RfcJoin (channel); 182 } else { 183 SparkleHelpers.DebugInfo ("ListenerIrc", "Dangerous channel, change the setting to access"); 184 } 185 } 186 this.client.RfcMode (channel, "+s"); 187 } 188 } 189 } 190 191 192 public override void Announce (SparkleAnnouncement announcement) 193 { 194 string channel = "#" + announcement.FolderIdentifier; 195 this.client.SendMessage (SendType.Message, channel, announcement.Message); 196 197 // Also announce to ourselves for debugging purposes 198 // base.OnAnnouncement (announcement); 199 } 200 201 202 public override void Dispose () 203 { 204 this.thread.Abort (); 205 this.thread.Join (); 206 base.Dispose (); 207 } 208 209 210 // Creates a SHA-1 hash of input 211 private string SHA1 (string s) 212 { 213 SHA1 sha1 = new SHA1CryptoServiceProvider (); 214 Byte[] bytes = ASCIIEncoding.Default.GetBytes (s); 215 Byte[] encoded_bytes = sha1.ComputeHash (bytes); 216 return BitConverter.ToString (encoded_bytes).ToLower ().Replace ("-", ""); 217 } 218 } 219}