/SparkleLib/SparkleListenerIrc.cs

http://github.com/hbons/SparkleShare · 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. using System;
  17. using System.Text;
  18. using System.Threading;
  19. using System.Security.Cryptography;
  20. using Meebey.SmartIrc4net;
  21. namespace SparkleLib {
  22. public class SparkleListenerIrc : SparkleListenerBase {
  23. private Thread thread;
  24. private IrcClient client;
  25. private string nick;
  26. private string announcements_password;
  27. private bool allow_passwordless_join;
  28. public SparkleListenerIrc (Uri server, string folder_identifier) :
  29. base (server, folder_identifier)
  30. {
  31. // Try to get a uniqueish nickname
  32. this.nick = SHA1 (DateTime.Now.ToString ("ffffff") + "sparkles");
  33. // Most irc servers don't allow nicknames starting
  34. // with a number, so prefix an alphabetic character
  35. this.nick = "s" + this.nick.Substring (0, 7);
  36. // Optional password to make the channel access more safe
  37. this.announcements_password =
  38. SparkleConfig.DefaultConfig.GetConfigOption ("announcements_password");
  39. // Option to allow access to channel when no password is defined
  40. try {
  41. string option = SparkleConfig.DefaultConfig.GetConfigOption ("allow_passwordless_join");
  42. this.allow_passwordless_join = (option == null || Convert.ToBoolean (option));
  43. } catch (Exception) {
  44. this.allow_passwordless_join = true;
  45. }
  46. base.channels.Add ("#" + folder_identifier);
  47. this.client = new IrcClient () {
  48. PingTimeout = 180,
  49. PingInterval = 60
  50. };
  51. string proxy = Environment.GetEnvironmentVariable ("http_proxy");
  52. Uri proxy_uri = null;
  53. if (!String.IsNullOrEmpty (proxy) &&
  54. Uri.TryCreate (proxy, UriKind.Absolute, out proxy_uri)) {
  55. if (proxy_uri.Scheme == "http") {
  56. this.client.ProxyType = ProxyType.Http;
  57. this.client.ProxyHost = proxy_uri.Host;
  58. this.client.ProxyPort = proxy_uri.Port;
  59. }
  60. }
  61. this.client.OnConnected += delegate {
  62. base.is_connecting = false;
  63. OnConnected ();
  64. };
  65. this.client.OnDisconnected += delegate {
  66. base.is_connecting = false;
  67. OnDisconnected ();
  68. };
  69. this.client.OnError += delegate {
  70. base.is_connecting = false;
  71. OnDisconnected ();
  72. };
  73. this.client.OnChannelMessage += delegate (object o, IrcEventArgs args) {
  74. string message = args.Data.Message.Trim ();
  75. string folder_id = args.Data.Channel.Substring (1); // remove the starting hash
  76. OnAnnouncement (new SparkleAnnouncement (folder_id, message));
  77. };
  78. }
  79. public override bool IsConnected {
  80. get {
  81. return this.client.IsConnected;
  82. }
  83. }
  84. // Starts a new thread and listens to the channel
  85. public override void Connect ()
  86. {
  87. SparkleHelpers.DebugInfo ("ListenerIrc", "Connecting to " + Server);
  88. base.is_connecting = true;
  89. this.thread = new Thread (
  90. new ThreadStart (delegate {
  91. try {
  92. // Connect, login, and join the channel
  93. int port = base.server.Port;
  94. if (port < 0)
  95. port = 6667;
  96. this.client.Connect (base.server.Host, port);
  97. this.client.Login (this.nick, this.nick, 8, this.nick);
  98. foreach (string channel in base.channels) {
  99. SparkleHelpers.DebugInfo ("ListenerIrc", "Joining channel " + channel);
  100. if (!string.IsNullOrEmpty (this.announcements_password)) {
  101. SparkleHelpers.DebugInfo ("ListenerIrc", "Password set to access the channel");
  102. this.client.RfcJoin (channel, this.announcements_password);
  103. this.client.RfcMode (channel, "+k " + this.announcements_password);
  104. } else {
  105. if (this.allow_passwordless_join) {
  106. SparkleHelpers.DebugInfo ("ListenerIrc", "Accessing unprotected channel, change the setting to not access");
  107. this.client.RfcJoin (channel);
  108. } else {
  109. SparkleHelpers.DebugInfo ("ListenerIrc", "Unprotected channel, change the setting to access");
  110. base.is_connecting = false;
  111. OnDisconnected ();
  112. throw new ConnectionException ("Unprotected channel, change the setting to access");
  113. }
  114. }
  115. this.client.RfcMode (channel, "+s");
  116. }
  117. // List to the channel, this blocks the thread
  118. this.client.Listen ();
  119. // Disconnect when we time out
  120. this.client.Disconnect ();
  121. } catch (ConnectionException e) {
  122. SparkleHelpers.DebugInfo ("ListenerIrc", "Could not connect to " + Server + ": " + e.Message);
  123. }
  124. })
  125. );
  126. this.thread.Start ();
  127. }
  128. public override void AlsoListenTo (string folder_identifier)
  129. {
  130. string channel = "#" + folder_identifier;
  131. if (!base.channels.Contains (channel)) {
  132. base.channels.Add (channel);
  133. if (IsConnected) {
  134. SparkleHelpers.DebugInfo ("ListenerIrc", "Joining channel " + channel);
  135. if (this.announcements_password != null) {
  136. SparkleHelpers.DebugInfo ("ListenerIrc", "Password set to access the channel");
  137. this.client.RfcJoin (channel, this.announcements_password);
  138. this.client.RfcMode (channel, "+k " + this.announcements_password);
  139. } else {
  140. if (allow_passwordless_join) {
  141. SparkleHelpers.DebugInfo ("ListenerIrc", "Accessing a dangerous channel change the setting to not access");
  142. this.client.RfcJoin (channel);
  143. } else {
  144. SparkleHelpers.DebugInfo ("ListenerIrc", "Dangerous channel, change the setting to access");
  145. }
  146. }
  147. this.client.RfcMode (channel, "+s");
  148. }
  149. }
  150. }
  151. public override void Announce (SparkleAnnouncement announcement)
  152. {
  153. string channel = "#" + announcement.FolderIdentifier;
  154. this.client.SendMessage (SendType.Message, channel, announcement.Message);
  155. // Also announce to ourselves for debugging purposes
  156. // base.OnAnnouncement (announcement);
  157. }
  158. public override void Dispose ()
  159. {
  160. this.thread.Abort ();
  161. this.thread.Join ();
  162. base.Dispose ();
  163. }
  164. // Creates a SHA-1 hash of input
  165. private string SHA1 (string s)
  166. {
  167. SHA1 sha1 = new SHA1CryptoServiceProvider ();
  168. Byte[] bytes = ASCIIEncoding.Default.GetBytes (s);
  169. Byte[] encoded_bytes = sha1.ComputeHash (bytes);
  170. return BitConverter.ToString (encoded_bytes).ToLower ().Replace ("-", "");
  171. }
  172. }
  173. }