Permalink
Cannot retrieve contributors at this time
/* | |
* Smuxi - Smart MUltipleXed Irc | |
* | |
* Copyright (c) 2005-2016 Mirco Bauer <meebey@meebey.net> | |
* | |
* Full GPL License: <http://www.gnu.org/licenses/gpl.txt> | |
* | |
* This program is free software; you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation; either version 2 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program; if not, write to the Free Software | |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
*/ | |
using System; | |
using System.IO; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Security.Cryptography.X509Certificates; | |
using System.Globalization; | |
using System.Threading; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Meebey.SmartIrc4net; | |
using Smuxi.Common; | |
using IrcProxyType = Meebey.SmartIrc4net.ProxyType; | |
namespace Smuxi.Engine | |
{ | |
[ProtocolManagerInfo(Name = "IRC", Description = "Internet Relay Chat", Alias = "irc")] | |
public class IrcProtocolManager : ProtocolManagerBase | |
{ | |
#if LOG4NET | |
private static readonly log4net.ILog _Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | |
#endif | |
private static readonly string _LibraryTextDomain = "smuxi-engine-irc"; | |
private IrcFeatures _IrcClient; | |
private ServerModel _ServerModel; | |
private string _Host; | |
private int _Port; | |
private string _Network; | |
private string[] _Nicknames; | |
string _Realname; | |
private int _CurrentNickname; | |
private string _Username; | |
private string _Password; | |
private FrontendManager _FrontendManager; | |
private bool _Listening; | |
private ChatModel _NetworkChat; | |
private TimeSpan _LastLag; | |
private Thread _RunThread; | |
private Thread _LagWatcherThread; | |
private TaskQueue _ChannelJoinQueue = new TaskQueue("JoinChannelQueue"); | |
private List<string> _QueuedChannelJoinList = new List<string>(); | |
private List<string> _ActiveChannelJoinList = new List<string>(); | |
private AutoResetEvent _ActiveChannelJoinHandle = new AutoResetEvent(false); | |
bool HasListMaskSearchSupport { get; set; } | |
bool HasSafeListSupport { get; set; } | |
IList<ChannelInfo> NetworkChannels { get; set; } | |
DateTime NetworkChannelsAge { get; set; } | |
TimeSpan NetworkChannelsMaxAge { get; set; } | |
List<string> ChannelTypes { get; set; } | |
Dictionary<string, string> ChannelKeys { get; set; } | |
public override bool IsConnected { | |
get { | |
if ((_IrcClient != null) && | |
(_IrcClient.IsConnected)) { | |
return true; | |
} | |
return false; | |
} | |
} | |
public override string Host { | |
get { | |
if (_IrcClient == null) { | |
return null; | |
} | |
return _IrcClient.Address; | |
} | |
} | |
public override int Port { | |
get { | |
if (_IrcClient == null) { | |
return -1; | |
} | |
return _IrcClient.Port; | |
} | |
} | |
public override string NetworkID { | |
get { | |
if (String.IsNullOrEmpty(_Network)) { | |
return _IrcClient.Address; | |
} | |
return _Network; | |
} | |
} | |
public override string Protocol { | |
get { | |
return "IRC"; | |
} | |
} | |
public override ChatModel Chat { | |
get { | |
return _NetworkChat; | |
} | |
} | |
private string Prefix { | |
get { | |
if (_IrcClient == null) { | |
return String.Empty; | |
} | |
if (IrcMe == null) { | |
return _IrcClient.Nickname; | |
} | |
return String.Format("{0}!{1}@{2}", _IrcClient.Nickname, | |
IrcMe.Ident, IrcMe.Host); | |
} | |
} | |
private IrcPersonModel IrcMe { | |
get { | |
return (IrcPersonModel) Me; | |
} | |
set { | |
Me = value; | |
} | |
} | |
public IrcProtocolManager(Session session) : base(session) | |
{ | |
Trace.Call(session); | |
NetworkChannelsMaxAge = TimeSpan.FromMinutes(5); | |
ChannelTypes = new List<string>(new string[] {"#", "&", "!", "+"}); | |
ChannelKeys = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); | |
_IrcClient = new IrcFeatures(); | |
_IrcClient.AutoRetry = true; | |
// keep retrying to connect forever | |
_IrcClient.AutoRetryLimit = 0; | |
_IrcClient.AutoRetryDelay = 120; | |
_IrcClient.AutoReconnect = true; | |
_IrcClient.AutoRelogin = true; | |
// we need to keep track of channel keys for proper auto rejoin | |
_IrcClient.AutoRejoin = false; | |
// HACK: SmartIrc4net <= 0.4.5.1 is not resetting the nickname list | |
// after disconnect. This causes random nicks to be used when there | |
// are many reconnects like when the network connection goes flaky, | |
// see: http://projects.qnetp.net/issues/show/163 | |
_IrcClient.AutoNickHandling = false; | |
_IrcClient.CtcpVersion = Engine.VersionString; | |
_IrcClient.SendDelay = 250; | |
_IrcClient.SupportNonRfc = true; | |
_IrcClient.OnRawMessage += new IrcEventHandler(_OnRawMessage); | |
_IrcClient.OnChannelMessage += new IrcEventHandler(_OnChannelMessage); | |
_IrcClient.OnChannelAction += new ActionEventHandler(_OnChannelAction); | |
_IrcClient.OnChannelNotice += new IrcEventHandler(_OnChannelNotice); | |
_IrcClient.OnQueryMessage += new IrcEventHandler(_OnQueryMessage); | |
_IrcClient.OnQueryAction += new ActionEventHandler(_OnQueryAction); | |
_IrcClient.OnQueryNotice += new IrcEventHandler(_OnQueryNotice); | |
_IrcClient.OnJoin += new JoinEventHandler(_OnJoin); | |
_IrcClient.OnNames += new NamesEventHandler(_OnNames); | |
_IrcClient.OnPart += new PartEventHandler(_OnPart); | |
_IrcClient.OnKick += new KickEventHandler(_OnKick); | |
_IrcClient.OnNickChange += new NickChangeEventHandler(_OnNickChange); | |
_IrcClient.OnOwner += new OwnerEventHandler(OnOwner); | |
_IrcClient.OnDeowner += new DeownerEventHandler(OnDeowner); | |
_IrcClient.OnChannelAdmin += new ChannelAdminEventHandler(OnChannelAdmin); | |
_IrcClient.OnDeChannelAdmin += new DeChannelAdminEventHandler(OnDeChannelAdmin); | |
_IrcClient.OnOp += new OpEventHandler(_OnOp); | |
_IrcClient.OnDeop += new DeopEventHandler(_OnDeop); | |
_IrcClient.OnHalfop += new HalfopEventHandler(OnHalfop); | |
_IrcClient.OnDehalfop += new DehalfopEventHandler(OnDehalfop); | |
_IrcClient.OnVoice += new VoiceEventHandler(_OnVoice); | |
_IrcClient.OnDevoice += new DevoiceEventHandler(_OnDevoice); | |
_IrcClient.OnUserModeChange += OnUserModeChange; | |
_IrcClient.OnChannelModeChange += OnChannelModeChange; | |
_IrcClient.OnTopic += new TopicEventHandler(_OnTopic); | |
_IrcClient.OnTopicChange += new TopicChangeEventHandler(_OnTopicChange); | |
_IrcClient.OnQuit += new QuitEventHandler(_OnQuit); | |
_IrcClient.OnRegistered += new EventHandler(_OnRegistered); | |
_IrcClient.OnDisconnected += new EventHandler(_OnDisconnected); | |
_IrcClient.OnAutoConnectError += OnAutoConnectError; | |
_IrcClient.OnAway += new AwayEventHandler(_OnAway); | |
_IrcClient.OnUnAway += new IrcEventHandler(_OnUnAway); | |
_IrcClient.OnNowAway += new IrcEventHandler(_OnNowAway); | |
_IrcClient.OnCtcpRequest += new CtcpEventHandler(_OnCtcpRequest); | |
_IrcClient.OnCtcpReply += new CtcpEventHandler(_OnCtcpReply); | |
_IrcClient.OnWho += OnWho; | |
_IrcClient.OnInvite += OnInvite; | |
_IrcClient.OnReadLine += OnReadLine; | |
_IrcClient.OnWriteLine += OnWriteLine; | |
_IrcClient.CtcpUserInfo = (string) Session.UserConfig["Connection/Realname"]; | |
// disabled as we don't use / support DCC yet | |
_IrcClient.CtcpDelegates.Remove("dcc"); | |
// finger we handle ourself, no little helga here! | |
_IrcClient.CtcpDelegates["finger"] = delegate(CtcpEventArgs e) { | |
_IrcClient.SendMessage( | |
SendType.CtcpReply, e.Data.Nick, | |
String.Format("{0} {1}", | |
e.CtcpCommand, | |
_IrcClient.CtcpUserInfo | |
) | |
); | |
}; | |
// time we handle ourself | |
_IrcClient.CtcpDelegates["time"] = delegate(CtcpEventArgs e) { | |
_IrcClient.SendMessage( | |
SendType.CtcpReply, e.Data.Nick, | |
String.Format("{0} {1}", | |
e.CtcpCommand, | |
DateTime.Now.ToString( | |
"ddd MMM dd HH:mm:ss yyyy", | |
DateTimeFormatInfo.InvariantInfo | |
) | |
) | |
); | |
}; | |
} | |
private void OnWho(object sender, WhoEventArgs e) | |
{ | |
if (e.WhoInfo.Nick == _IrcClient.Nickname) { | |
// that's me! | |
IrcMe.Ident = e.WhoInfo.Ident; | |
IrcMe.Host = e.WhoInfo.Host; | |
IrcMe.RealName = e.WhoInfo.Realname; | |
} | |
} | |
private void OnInvite(object sender, InviteEventArgs e) | |
{ | |
var builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.MessageType = MessageType.Normal; | |
var msg = builder.CreateFormat(_("{0} invites you to {1}"), | |
CreatePerson(e.Who), e.Channel); | |
foreach (var part in msg) { | |
part.IsHighlight = true; | |
} | |
builder.Append(msg); | |
Session.AddMessageToChat(_NetworkChat, builder.ToMessage()); | |
builder = CreateMessageBuilder(); | |
string host; | |
if (String.IsNullOrEmpty(NetworkID)) { | |
host = _IrcClient.Address; | |
} else { | |
host = NetworkID; | |
var serverSettings = new ServerListController(Session.UserConfig); | |
var server = serverSettings.GetServerByNetwork(host); | |
if (server == null) { | |
// if the network is not stored in config, we need to | |
// fallback to the bare server address. Otherwise the | |
// frontend will have no idea how to connect to it. | |
host = _IrcClient.Address; | |
} | |
} | |
string url = String.Format("irc://{0}/{1}", host, e.Channel); | |
builder.AppendUrl(url, _("Accept invite (join room)")); | |
Session.AddMessageToChat(_NetworkChat, builder.ToMessage()); | |
} | |
void OnReadLine(object sender, ReadLineEventArgs e) | |
{ | |
DebugRead(e.Line); | |
} | |
void OnWriteLine(object sender, WriteLineEventArgs e) | |
{ | |
DebugWrite(e.Line); | |
} | |
public override string ToString() | |
{ | |
string result = null; | |
if (_IrcClient != null) { | |
if (String.IsNullOrEmpty(_Network)) { | |
result += _IrcClient.Address; | |
} else { | |
result += _Network; | |
} | |
} | |
result += " (IRC)"; | |
if (IsConnected) { | |
if (_IrcClient.IsAway) { | |
result += " (" + _("away") + ")"; | |
} | |
if (_IrcClient.Lag > TimeSpan.FromSeconds(5)) { | |
result += String.Format(" ({0})", | |
String.Format( | |
// TRANSLATOR: {0} is the amount of seconds | |
_("lag: {0} seconds"), | |
(int) _IrcClient.Lag.TotalSeconds | |
) | |
); | |
} | |
} else { | |
result += " (" + _("not connected") + ")"; | |
} | |
return result; | |
} | |
public override void Connect(FrontendManager fm, ServerModel server) | |
{ | |
Trace.Call(fm, server); | |
if (server == null) { | |
throw new ArgumentNullException("server"); | |
} | |
_FrontendManager = fm; | |
_ServerModel = server; | |
ApplyConfig(Session.UserConfig, server); | |
// TODO: use config for single network chat or once per network manager | |
_NetworkChat = Session.CreateChat<ProtocolChatModel>( | |
_Network, "IRC " + _Network, this | |
); | |
// BUG: race condition when we use Session.AddChat() as it pushes this already | |
// to the connected frontend and the frontend will sync and get the page 2 times! | |
//Session.Chats.Add(_NetworkChat); | |
// NOTABUG: the frontend manager needs to take care for that | |
Session.AddChat(_NetworkChat); | |
Session.SyncChat(_NetworkChat); | |
_RunThread = new Thread(new ThreadStart(_Run)); | |
_RunThread.IsBackground = true; | |
_RunThread.Name = String.Format( | |
"IrcProtocolManager ({0}:{1}) listener", | |
server.Hostname, server.Port | |
); | |
_RunThread.Start(); | |
_LagWatcherThread = new Thread(new ThreadStart(_LagWatcher)); | |
_LagWatcherThread.IsBackground = true; | |
_LagWatcherThread.Name = String.Format( | |
"IrcProtocolManager ({0}:{1}) lag watcher", | |
server.Hostname, server.Port | |
); | |
_LagWatcherThread.Start(); | |
} | |
public void Connect(FrontendManager fm) | |
{ | |
Trace.Call(fm); | |
MessageBuilder builder; | |
try { | |
if (!String.IsNullOrEmpty(_IrcClient.ProxyHost)) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("Using proxy: {0}:{1}"), | |
_IrcClient.ProxyHost, | |
_IrcClient.ProxyPort); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
} | |
string msg; | |
msg = String.Format(_("Connecting to {0} port {1}..."), _Host, _Port); | |
if (fm != null) { | |
fm.SetStatus(msg); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix().AppendText(msg); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
// TODO: add SSL support | |
_IrcClient.Connect(_Host, _Port); | |
if (fm != null) { | |
fm.UpdateNetworkStatus(); | |
} | |
msg = String.Format(_("Connection to {0} established"), _Host); | |
if (fm != null) { | |
fm.SetStatus(msg); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix().AppendText(msg); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix().AppendText(_("Logging in...")); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
string realname = _Realname; | |
if (realname == null || realname.Trim().Length == 0) { | |
realname = "unset"; | |
} | |
if (!Regex.IsMatch(_Username, "^[a-z0-9]+$", RegexOptions.IgnoreCase)) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendWarningText( | |
"Warning: Your username (ident) contains special " + | |
"characters which the IRC server might refuse. " + | |
"If this happens please change your username in the " + | |
"server settings." | |
); | |
Session.AddMessageToChat(_NetworkChat, builder.ToMessage()); | |
} | |
_IrcClient.Login(_Nicknames, realname, 0, _Username, _Password); | |
// set Me property very early as we might need to know who we | |
// are before the registration was confirmed in _OnRegistered() | |
Me = CreatePerson(_IrcClient.Nickname); | |
foreach (string command in (string[]) Session.UserConfig["Connection/OnConnectCommands"]) { | |
if (command.Length == 0) { | |
continue; | |
} | |
CommandModel cd = new CommandModel(_FrontendManager, _NetworkChat, | |
(string) Session.UserConfig["Interface/Entry/CommandCharacter"], | |
command); | |
bool handled; | |
handled = Session.Command(cd); | |
if (!handled) { | |
Command(cd); | |
} | |
} | |
_Listening = true; | |
} catch (CouldNotConnectException ex) { | |
if (fm != null) { | |
fm.SetStatus(_("Connection failed!")); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("Connection failed! Reason: ")); | |
builder.AppendText(ex.Message); | |
if (ex.InnerException is IOException && | |
ex.InnerException.InnerException != null && | |
ex.InnerException.InnerException.GetType().FullName == "Mono.Security.Protocol.Tls.TlsException") { | |
// this is a CA / certificate issue | |
builder.AppendSpace(); | |
builder.AppendUrl( | |
"https://smuxi.im/faq/troubleshooting/linux-tls/", | |
"[" + _("Open Smuxi FAQ for help") + "]" | |
); | |
} | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
throw; | |
} | |
} | |
public override void Disconnect(FrontendManager fm) | |
{ | |
Trace.Call(fm); | |
MessageBuilder builder; | |
if (fm != null) { | |
fm.SetStatus(_("Disconnecting...")); | |
} | |
if (IsConnected) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("Disconnecting from {0}..."), | |
_IrcClient.Address); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
// else the Listen() thread would try to connect again | |
_Listening = false; | |
_IrcClient.Disconnect(); | |
if (fm != null) { | |
fm.SetStatus(String.Format(_("Disconnected from {0}"), | |
_IrcClient.Address)); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("Connection closed")); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
// TODO: set someone else as current network manager? | |
} else { | |
if (fm != null) { | |
fm.SetStatus(String.Empty); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("Not connected")); | |
Session.AddMessageToFrontend(fm, Chat, builder.ToMessage()); | |
} | |
if (_RunThread != null && _RunThread.IsAlive) { | |
try { | |
_RunThread.Abort(); | |
} catch (Exception ex) { | |
#if LOG4NET | |
_Logger.Error("_RunThread.Abort() failed:", ex); | |
#endif | |
} | |
} | |
if (_LagWatcherThread != null && _LagWatcherThread.IsAlive) { | |
try { | |
_LagWatcherThread.Abort(); | |
} catch (Exception ex) { | |
#if LOG4NET | |
_Logger.Error("_LagWatcherThread.Abort() failed:", ex); | |
#endif | |
} | |
} | |
if (fm != null) { | |
fm.UpdateNetworkStatus(); | |
} | |
} | |
public override void Reconnect(FrontendManager fm) | |
{ | |
Trace.Call(fm); | |
MessageBuilder builder; | |
if (fm != null) { | |
fm.SetStatus(_("Reconnecting...")); | |
} | |
try { | |
string msg; | |
if (_IrcClient != null) { | |
if (_IrcClient.IsConnected) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("Reconnecting to {0}..."), | |
_IrcClient.Address); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
ApplyConfig(Session.UserConfig, _ServerModel); | |
_IrcClient.Reconnect(true); | |
msg = String.Format(_("Connection to {0} established"), | |
_IrcClient.Address); | |
if (fm != null) { | |
fm.SetStatus(msg); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix().AppendText(msg); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
} else { | |
Connect(fm); | |
} | |
} else { | |
msg = _("Reconnect Error"); | |
if (fm != null) { | |
fm.SetStatus(msg); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix().AppendText(msg); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
} | |
} catch (ConnectionException) { | |
if (fm != null) { | |
fm.SetStatus(String.Empty); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix().AppendText(_("Not connected")); | |
Session.AddMessageToFrontend(fm, Chat, builder.ToMessage()); | |
} | |
if (fm != null) { | |
fm.UpdateNetworkStatus(); | |
} | |
} | |
public override void Dispose() | |
{ | |
Trace.Call(); | |
_ChannelJoinQueue.Dispose(); | |
base.Dispose(); | |
} | |
public override IList<GroupChatModel> FindGroupChats(GroupChatModel filter) | |
{ | |
Trace.Call(filter); | |
// invalidate channel list cache when too old | |
if (NetworkChannels != null && | |
(DateTime.UtcNow - NetworkChannelsAge) > NetworkChannelsMaxAge) { | |
NetworkChannels = null; | |
} | |
string searchPattern = null; | |
if (filter == null || String.IsNullOrEmpty(filter.Name)) { | |
// full channel list | |
} else { | |
if (!filter.Name.StartsWith("*") && !filter.Name.EndsWith("*")) { | |
searchPattern = String.Format("*{0}*", filter.Name); | |
} else { | |
searchPattern = filter.Name; | |
} | |
} | |
var channels = NetworkChannels; | |
if (channels == null && HasSafeListSupport) { | |
// fetch and cache full channel list from server | |
channels = _IrcClient.GetChannelList(String.Empty); | |
NetworkChannels = channels; | |
NetworkChannelsAge = DateTime.UtcNow; | |
} else if (channels == null && searchPattern != null && | |
HasListMaskSearchSupport) { | |
channels = _IrcClient.GetChannelList(searchPattern); | |
} else if (channels == null) { | |
// Houston, we have a problem | |
// no safelist and empty search pattern, the IRCd might kill us! | |
channels = _IrcClient.GetChannelList(String.Empty); | |
NetworkChannels = channels; | |
NetworkChannelsAge = DateTime.UtcNow; | |
} | |
List<GroupChatModel> chats = new List<GroupChatModel>(channels.Count); | |
foreach (ChannelInfo info in channels) { | |
if (channels == NetworkChannels && | |
searchPattern != null && | |
!Pattern.IsMatch(info.Channel, searchPattern)) { | |
continue; | |
} | |
GroupChatModel chat = new GroupChatModel( | |
info.Channel, | |
info.Channel, | |
null | |
); | |
chat.PersonCount = info.UserCount; | |
var topic = CreateMessageBuilder(); | |
topic.AppendMessage(info.Topic); | |
chat.Topic = topic.ToMessage(); | |
chats.Add(chat); | |
} | |
return chats; | |
} | |
public override void OpenChat(FrontendManager fm, ChatModel chat) | |
{ | |
Trace.Call(fm, chat); | |
CommandModel cmd = new CommandModel(fm, _NetworkChat, chat.ID); | |
switch (chat.ChatType) { | |
case ChatType.Person: | |
CommandQuery(cmd); | |
break; | |
case ChatType.Group: | |
CommandJoin(cmd); | |
break; | |
} | |
} | |
public override void CloseChat(FrontendManager fm, ChatModel chatInfo) | |
{ | |
Trace.Call(fm, chatInfo); | |
if (fm == null) { | |
throw new ArgumentNullException("fm"); | |
} | |
if (chatInfo == null) { | |
throw new ArgumentNullException("chatInfo"); | |
} | |
// get real chat object from session | |
var chat = GetChat(chatInfo.ID, chatInfo.ChatType); | |
if (chat == null) { | |
#if LOG4NET | |
_Logger.Error("CloseChat(): Session.GetChat(" + | |
chatInfo.ID + ", " + chatInfo.ChatType + ")" + | |
" returned null!"); | |
#endif | |
return; | |
} | |
if (!chat.IsEnabled) { | |
Session.RemoveChat(chat); | |
return; | |
} | |
switch (chat.ChatType) { | |
case ChatType.Person: | |
Session.RemoveChat(chat); | |
break; | |
case ChatType.Group: | |
CommandModel cmd = new CommandModel(fm, _NetworkChat, chat.ID); | |
CommandPart(cmd); | |
break; | |
} | |
} | |
public override void SetPresenceStatus(PresenceStatus status, | |
string message) | |
{ | |
Trace.Call(status, message); | |
if (!_IrcClient.IsConnected) { | |
return; | |
} | |
switch (status) { | |
case PresenceStatus.Online: | |
if (!_IrcClient.IsAway) { | |
// nothing to do | |
return; | |
} | |
_IrcClient.RfcAway(); | |
break; | |
case PresenceStatus.Away: | |
if (String.IsNullOrEmpty(message)) { | |
// HACK: empty away message unsets away state on IRC | |
message = "away"; | |
} | |
_IrcClient.RfcAway(message); | |
break; | |
} | |
base.SetPresenceStatus(status, message); | |
} | |
public override bool Command(CommandModel command) | |
{ | |
Trace.Call(command); | |
bool handled = false; | |
if (IsConnected) { | |
if (command.IsCommand) { | |
// commands which work when we have a connection | |
switch (command.Command) { | |
case "help": | |
CommandHelp(command); | |
handled = true; | |
break; | |
// commands which work on serverchat/channels/queries | |
case "j": | |
case "join": | |
CommandJoin(command); | |
handled = true; | |
break; | |
case "msg": | |
CommandMessage(command); | |
handled = true; | |
break; | |
case "query": | |
CommandQuery(command); | |
handled = true; | |
break; | |
case "amsg": | |
CommandAllMessage(command); | |
handled = true; | |
break; | |
case "anotice": | |
CommandAllNotice(command); | |
handled = true; | |
break; | |
case "ame": | |
CommandAllMe(command); | |
handled = true; | |
break; | |
case "notice": | |
CommandNotice(command); | |
handled = true; | |
break; | |
case "nick": | |
CommandNick(command); | |
handled = true; | |
break; | |
case "raw": | |
case "quote": | |
CommandRaw(command); | |
handled = true; | |
break; | |
case "ping": | |
CommandPing(command); | |
handled = true; | |
break; | |
case "version": | |
CommandVersion(command); | |
handled = true; | |
break; | |
case "time": | |
CommandTime(command); | |
handled = true; | |
break; | |
case "finger": | |
CommandFinger(command); | |
handled = true; | |
break; | |
case "who": | |
CommandWho(command); | |
handled = true; | |
break; | |
case "whois": | |
CommandWhoIs(command); | |
handled = true; | |
break; | |
case "whowas": | |
CommandWhoWas(command); | |
handled = true; | |
break; | |
case "away": | |
CommandAway(command); | |
// send away on all other IRC networks too | |
lock (Session.ProtocolManagers) { | |
foreach (IProtocolManager nm in Session.ProtocolManagers) { | |
if (nm == this) { | |
// skip us, else we send it 2 times | |
continue; | |
} | |
if (nm is IrcProtocolManager) { | |
IrcProtocolManager ircnm = (IrcProtocolManager)nm; | |
ircnm.CommandAway(command); | |
} | |
} | |
} | |
handled = true; | |
break; | |
case "ctcp": | |
CommandCtcp(command); | |
handled = true; | |
break; | |
case "oper": | |
CommandOper(command); | |
handled = true; | |
break; | |
// commands which only work on channels or queries | |
case "me": | |
CommandMe(command); | |
handled = true; | |
break; | |
case "say": | |
CommandSay(command); | |
handled = true; | |
break; | |
// commands which only work on channels | |
case "p": | |
case "part": | |
CommandPart(command); | |
handled = true; | |
break; | |
case "topic": | |
CommandTopic(command); | |
handled = true; | |
break; | |
case "cycle": | |
case "rejoin": | |
CommandCycle(command); | |
handled = true; | |
break; | |
case "op": | |
CommandOp(command); | |
handled = true; | |
break; | |
case "deop": | |
CommandDeop(command); | |
handled = true; | |
break; | |
case "owner": | |
CommandOwner(command); | |
handled = true; | |
break; | |
case "deowner": | |
CommandDeowner(command); | |
handled = true; | |
break; | |
case "chanadmin": | |
CommandChanAdmin(command); | |
handled = true; | |
break; | |
case "dechanadmin": | |
CommandDeChanAdmin(command); | |
handled = true; | |
break; | |
case "halfop": | |
CommandHalfop(command); | |
handled = true; | |
break; | |
case "dehalfop": | |
CommandDehalfop(command); | |
handled = true; | |
break; | |
case "voice": | |
CommandVoice(command); | |
handled = true; | |
break; | |
case "devoice": | |
CommandDevoice(command); | |
handled = true; | |
break; | |
case "ban": | |
CommandBan(command); | |
handled = true; | |
break; | |
case "unban": | |
CommandUnban(command); | |
handled = true; | |
break; | |
case "banexcept": | |
CommandBanException(command); | |
handled = true; | |
break; | |
case "unbanexcept": | |
CommandUnBanException(command); | |
handled = true; | |
break; | |
case "inviteexcept": | |
CommandInviteException(command); | |
handled = true; | |
break; | |
case "uninviteexcept": | |
CommandUnInviteException(command); | |
handled = true; | |
break; | |
case "kick": | |
CommandKick(command); | |
handled = true; | |
break; | |
case "kickban": | |
case "kb": | |
CommandKickban(command); | |
handled = true; | |
break; | |
case "mode": | |
CommandMode(command); | |
handled = true; | |
break; | |
case "invite": | |
CommandInvite(command); | |
handled = true; | |
break; | |
case "names": | |
CommandNames(command); | |
handled = true; | |
break; | |
case "quit": | |
CommandQuit(command); | |
handled = true; | |
break; | |
case "sleep": | |
int amount = 0; | |
if (Int32.TryParse(command.Parameter, out amount)) { | |
var msg = CreateMessageBuilder(). | |
AppendEventPrefix(). | |
AppendText( | |
_("Sleeping for {0} milliseconds"), | |
amount | |
). | |
ToMessage(); | |
Session.AddMessageToChat(Chat, msg); | |
Thread.Sleep(amount); | |
} else { | |
_NotEnoughParameters(command); | |
} | |
handled = true; | |
break; | |
} | |
} else { | |
// normal text | |
if (command.Chat.ChatType == ChatType.Session || | |
command.Chat.ChatType == ChatType.Protocol) { | |
// we are on the session chat or protocol chat | |
_IrcClient.WriteLine(command.Data); | |
} else { | |
// split multiline messages | |
string[] lines = command.Data.Split(new char[] {'\n'}); | |
foreach (string line in lines) { | |
// split too long messages | |
var messages = SplitMessage("PRIVMSG", command.Chat.ID, | |
line); | |
foreach (string message in messages) { | |
_Say(command.Chat, message); | |
} | |
} | |
} | |
handled = true; | |
} | |
} else { | |
if (command.IsCommand) { | |
// commands which work even without beeing connected | |
switch (command.Command) { | |
case "help": | |
CommandHelp(command); | |
handled = true; | |
break; | |
case "connect": | |
CommandConnect(command); | |
handled = true; | |
break; | |
} | |
} else { | |
// normal text, without connection | |
_NotConnected(command); | |
handled = true; | |
} | |
} | |
return handled; | |
} | |
private void CommandFallback(CommandModel cmd) | |
{ | |
string parameters; | |
if (cmd.DataArray.Length <= 3) { | |
parameters = cmd.Parameter; | |
} else { | |
parameters = String.Format("{0} :{1}", | |
cmd.DataArray[1], | |
String.Join(" ", | |
cmd.DataArray, 2, | |
cmd.DataArray.Length - 2)); | |
} | |
string data = String.Format("{0}raw {1} {2}", | |
cmd.CommandCharacter, | |
cmd.Command, | |
parameters); | |
CommandModel command = new CommandModel( | |
cmd.FrontendManager, | |
cmd.Chat, | |
cmd.CommandCharacter, | |
data | |
); | |
CommandRaw(command); | |
} | |
public void CommandHelp(CommandModel cd) | |
{ | |
var builder = CreateMessageBuilder(); | |
// TRANSLATOR: this line is used as label / category for a | |
// list of commands below | |
builder.AppendHeader(_("IrcProtocolManager Commands")); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
string[] help = { | |
"connect irc server [port|+port] [password] [nicknames]", | |
"say", | |
"join/j channel(s) [key]", | |
"part/p [channel(s)] [part-message]", | |
"topic [new-topic]", | |
"names", | |
"cycle/rejoin", | |
"msg/query (channel|nick) message", | |
"amsg message", | |
"me action-message", | |
"ame action-message", | |
"notice (channel|nick) message", | |
"anotice message", | |
"invite nick [channel]", | |
"who nick/channel", | |
"whois nick", | |
"whowas nick", | |
"ping nick", | |
"version nick", | |
"time nick", | |
"finger nick", | |
"mode [target] [new-mode]", | |
"away [away-message]", | |
"oper username password", | |
"kick nick(s) [reason]", | |
"kickban/kb nick(s) [reason]", | |
"ban [mask]", | |
"unban mask", | |
"banexcept [mask]", | |
"unbanexcept mask", | |
"inviteexcept [mask]", | |
"uninviteexcept mask", | |
"voice nick", | |
"devoice nick", | |
"op nick", | |
"deop nick", | |
"owner nick", | |
"deowner nick", | |
"chanadmin nick", | |
"dechanadmin nick", | |
"halfop nick", | |
"dehalfop nick", | |
"nick newnick", | |
"ctcp destination command [data]", | |
"raw/quote irc-command", | |
"quit [quit-message]", | |
}; | |
foreach (string line in help) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(line); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
} | |
public void CommandConnect(CommandModel cd) | |
{ | |
FrontendManager fm = cd.FrontendManager; | |
var server = new IrcServerModel(); | |
if (cd.DataArray.Length >= 3) { | |
server.Hostname = cd.DataArray[2]; | |
} else { | |
server.Hostname = "localhost"; | |
} | |
if (cd.DataArray.Length >= 4) { | |
var port = cd.DataArray[3]; | |
var ssl = port.StartsWith("+"); | |
if (ssl) { | |
server.UseEncryption = true; | |
port = port.Substring(1); | |
} | |
try { | |
server.Port = Int32.Parse(port); | |
} catch (FormatException) { | |
var builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("Invalid port: {0}"), | |
cd.DataArray[3]); | |
Session.AddMessageToFrontend(fm, Chat, builder.ToMessage()); | |
return; | |
} | |
} else { | |
server.Port = 6667; | |
} | |
if (cd.DataArray.Length >= 5) { | |
server.Password = cd.DataArray[4]; | |
} | |
if (cd.DataArray.Length >= 6) { | |
var nicks = new List<string>(1); | |
nicks.Add(cd.DataArray[5]); | |
server.Nicknames = nicks; | |
} | |
Connect(fm, server); | |
} | |
public void CommandSay(CommandModel cd) | |
{ | |
_Say(cd.Chat, cd.Parameter); | |
} | |
private void _Say(ChatModel chat, string message) | |
{ | |
if (!chat.IsEnabled) { | |
return; | |
} | |
if (chat is PersonChatModel) { | |
PersonModel person = ((PersonChatModel) chat).Person; | |
IrcPersonModel ircperson = (IrcPersonModel) person; | |
ircperson.IsAway = false; | |
} | |
_IrcClient.SendMessage(SendType.Message, chat.ID, message); | |
var builder = CreateMessageBuilder(); | |
builder.AppendSenderPrefix(Me); | |
Match m = Regex.Match(message, String.Format(@"^@(?<nick>\S+)|^(?<nick>\S+)(?:\:|,)")); | |
if (m.Success && chat is GroupChatModel) { | |
// this is probably a reply with a nickname | |
string nick = m.Groups["nick"].Value; | |
#if LOG4NET | |
_Logger.Debug("_Say(): detected reply with possible nick: '" + nick + "' in: '" + m.Value + "'"); | |
#endif | |
var groupChat = (GroupChatModel) chat; | |
PersonModel person; | |
if (groupChat.Persons.TryGetValue(nick, out person)) { | |
// bingo, it's a nick on this channel | |
message = message.Substring(m.Value.Length); | |
var coloredNick = builder.CreateIdendityName(person); | |
coloredNick.Text = m.Value; | |
builder.AppendText(coloredNick); | |
} | |
} | |
builder.AppendMessage(message); | |
var msg = builder.ToMessage(); | |
Session.AddMessageToChat(chat, msg, true); | |
OnMessageSent( | |
new MessageEventArgs(chat, msg, null, chat.ID) | |
); | |
} | |
public void CommandJoin(CommandModel cd) | |
{ | |
Trace.Call(cd); | |
MessageBuilder builder; | |
if (cd.DataArray.Length < 2 || cd.DataArray[1].Length == 0) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
string[] channels = cd.DataArray[1].Split(','); | |
string[] keys = null; | |
if (cd.DataArray.Length > 2) { | |
keys = cd.DataArray[2].Split(','); | |
} | |
int activeCount; | |
lock (_ActiveChannelJoinList) { | |
activeCount = _ActiveChannelJoinList.Count; | |
} | |
if (activeCount > 0) { | |
// ok, these channels will be queued | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
// TRANSLATOR: some IRC networks dislike too many joins in a | |
// short period and thus Smuxi throttles/queues them | |
builder.AppendText(_("Queuing joins: {0}"), | |
String.Join(" ", channels)); | |
Session.AddMessageToFrontend(cd.FrontendManager, Chat, | |
builder.ToMessage()); | |
} | |
int i = 0; | |
foreach (string channel in channels) { | |
// HACK: copy channel from foreach() into our scope | |
var chan = channel; | |
var chanType = chan[0]; | |
if (!ChannelTypes.Contains(chanType.ToString())) { | |
chan = "#" + chan; | |
} | |
string key = keys != null && keys.Length > i ? keys[i] : null; | |
var chat = GetChat(chan, ChatType.Group); | |
if (chat != null && chat.IsEnabled) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText( | |
_("Already joined to channel: {0}." + | |
" Type /window {0} to switch to it."), | |
channel | |
); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
continue; | |
} | |
lock (_QueuedChannelJoinList) { | |
_QueuedChannelJoinList.Add(channel); | |
} | |
_ChannelJoinQueue.Queue(delegate { | |
try { | |
int count = 0; | |
string activeChans = null; | |
lock (_ActiveChannelJoinList) { | |
count = _ActiveChannelJoinList.Count; | |
if (count > 0) { | |
activeChans = String.Join( | |
" ", _ActiveChannelJoinList.ToArray() | |
); | |
} | |
} | |
if (count > 0) { | |
string queuedChans; | |
lock (_QueuedChannelJoinList) { | |
queuedChans = String.Join( | |
" ", _QueuedChannelJoinList.ToArray() | |
); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText( | |
_("Active joins: {0} - Queued joins: {1}"), | |
activeChans, queuedChans | |
); | |
Session.AddMessageToFrontend(cd.FrontendManager, Chat, | |
builder.ToMessage()); | |
#if LOG4NET | |
_Logger.Debug("CommandJoin(): waiting to join: " + chan); | |
#endif | |
_ActiveChannelJoinHandle.WaitOne(); | |
lock (_ActiveChannelJoinList) { | |
activeChans = String.Join( | |
" ", _ActiveChannelJoinList.ToArray() | |
); | |
} | |
lock (_QueuedChannelJoinList) { | |
_QueuedChannelJoinList.Remove(chan); | |
queuedChans = String.Join( | |
" ", _QueuedChannelJoinList.ToArray() | |
); | |
} | |
// TRANSLATORS: final message will look like this: | |
// Joining: #chan1 - Remaining active joins: #chan2 / queued joins: #chan3 | |
string msg = String.Format(_("Joining: {0}"), chan); | |
if (activeChans.Length > 0 || queuedChans.Length > 0) { | |
msg += String.Format(" - {0} ", _("Remaining")); | |
} | |
if (activeChans.Length > 0) { | |
msg += String.Format( | |
_("active joins: {0}"), | |
activeChans | |
); | |
} | |
if (queuedChans.Length > 0) { | |
if (activeChans.Length > 0) { | |
msg += " / "; | |
} | |
msg += String.Format( | |
_("queued joins: {0}"), | |
queuedChans | |
); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix().AppendText(msg); | |
Session.AddMessageToFrontend(cd.FrontendManager, Chat, | |
builder.ToMessage()); | |
} else { | |
lock (_QueuedChannelJoinList) { | |
_QueuedChannelJoinList.Remove(chan); | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("Joining: {0}"), chan); | |
Session.AddMessageToFrontend(cd.FrontendManager, Chat, | |
builder.ToMessage()); | |
} | |
#if LOG4NET | |
_Logger.Debug("CommandJoin(): joining: " + chan); | |
#endif | |
// we have a slot, show time! | |
if (key == null) { | |
_IrcClient.RfcJoin(chan); | |
} else { | |
_IrcClient.RfcJoin(chan, key); | |
lock (ChannelKeys) { | |
ChannelKeys[chan] = key; | |
} | |
} | |
// Some IRC networks are very kick happy and thus need | |
// some artificial delay between JOINs. | |
// We know our friendly networks though :) | |
string network = _Network == null ? String.Empty : _Network.ToLower(); | |
switch (network) { | |
case "efnet": | |
case "freenode": | |
case "gimpnet": | |
case "ircnet": | |
case "oftc": | |
// give the IRCd some time to actually sent us a JOIN | |
// confirmation, else we will just hammer all channels | |
// in a single row | |
_ActiveChannelJoinHandle.WaitOne(2 * 1000, false); | |
break; | |
default: | |
// delay the queue for some extra seconds so new join | |
// attempts will not happen too early as some IRCds | |
// limit this and disconnect us if we are not brave | |
Thread.Sleep(2000); | |
break; | |
} | |
} catch (ThreadAbortException ex) { | |
#if LOG4NET | |
_Logger.Warn("ThreadAbortException when trying to join channel: " | |
+ chan, ex); | |
#endif | |
} catch (Exception ex) { | |
#if LOG4NET | |
_Logger.Error("Exception when trying to join channel: " | |
+ chan, ex); | |
#endif | |
} | |
}); | |
i++; | |
} | |
} | |
public void CommandCycle(CommandModel cd) | |
{ | |
if (cd.Chat.ChatType == ChatType.Group) { | |
if (cd.Chat.IsEnabled) { | |
// disable chat so we don't loose the message buffer | |
Session.DisableChat(cd.Chat); | |
_IrcClient.RfcPart(cd.Chat.ID); | |
} | |
string key; | |
lock (ChannelKeys) { | |
ChannelKeys.TryGetValue(cd.Chat.ID, out key); | |
} | |
if (String.IsNullOrEmpty(key)) { | |
_IrcClient.RfcJoin(cd.Chat.ID); | |
} else { | |
_IrcClient.RfcJoin(cd.Chat.ID, key); | |
} | |
} | |
} | |
public void CommandMessage(CommandModel cd) | |
{ | |
Trace.Call(cd); | |
if ((cd.DataArray.Length >= 2) && | |
(cd.DataArray[1].Length >= 1)) { | |
var chanType = cd.DataArray[1][0].ToString(); | |
if (ChannelTypes.Contains(chanType)) { | |
// seems to be a channel | |
CommandMessageChannel(cd); | |
} else { | |
// seems to be a nick | |
CommandMessageNick(cd); | |
} | |
} else { | |
NotEnoughParameters(cd); | |
} | |
} | |
public void CommandQuery(CommandModel cd) | |
{ | |
ChatModel chat = null; | |
if (cd.DataArray.Length >= 2) { | |
string nickname = cd.DataArray[1]; | |
chat = GetChat(nickname, ChatType.Person); | |
if (chat == null) { | |
var person = CreatePerson(nickname); | |
chat = Session.CreatePersonChat(person, nickname, | |
nickname, this); | |
Session.AddChat(chat); | |
if (Session.IsLocal) { | |
Session.SyncChat(chat); | |
} else { | |
// HACK: lower probability of sync race condition swallowing | |
// messages, see: https://www.smuxi.org/issues/show/634 | |
ThreadPool.QueueUserWorkItem(delegate { | |
Thread.Sleep(1000); | |
Session.SyncChat(chat); | |
}); | |
} | |
} | |
} | |
if (cd.DataArray.Length >= 3) { | |
string message = String.Join(" ", cd.DataArray, 2, cd.DataArray.Length-2); | |
// ignore empty messages | |
if (message.TrimEnd(' ').Length > 0) { | |
_Say(chat, message); | |
} | |
} | |
} | |
public void CommandMessageChannel(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 3) { | |
string message = String.Join(" ", cd.DataArray, 2, cd.DataArray.Length-2); | |
string channelname = cd.DataArray[1]; | |
ChatModel chat = GetChat(channelname, ChatType.Group); | |
if (chat == null) { | |
// server chat as fallback if we are not joined | |
var builder = CreateMessageBuilder(); | |
builder.AppendText("<{0}:{1}> ", _IrcClient.Nickname, | |
channelname); | |
builder.AppendMessage(message); | |
Session.AddMessageToChat(Chat, builder.ToMessage(), true); | |
_IrcClient.SendMessage(SendType.Message, channelname, message); | |
} else { | |
_Say(chat, message); | |
} | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
[Obsolete("CommandMessageQuery() is deprecated, use CommandQuery() instead")] | |
public void CommandMessageQuery(CommandModel cmd) | |
{ | |
CommandQuery(cmd); | |
} | |
public void CommandMessageNick(CommandModel cmd) | |
{ | |
if (cmd == null) { | |
throw new ArgumentNullException("cmd"); | |
} | |
if (cmd.DataArray.Length < 3) { | |
NotEnoughParameters(cmd); | |
return; | |
} | |
var nickname = cmd.DataArray[1]; | |
string message = String.Join(" ", cmd.DataArray, 2, | |
cmd.DataArray.Length - 2); | |
// ignore empty messages | |
if (message.TrimEnd(' ').Length == 0) { | |
return; | |
} | |
var chat = GetChat(nickname, ChatType.Person); | |
if (chat == null) { | |
// fallback to protocol chat + where the command was issued | |
var msg = CreateMessageBuilder(). | |
AppendText("<{0}:{1}> ", _IrcClient.Nickname, nickname). | |
AppendMessage(message). | |
ToMessage(); | |
Session.AddMessageToChat(_NetworkChat, msg, true); | |
if (cmd.Chat != _NetworkChat && cmd.FrontendManager != null) { | |
cmd.FrontendManager.AddMessageToChat(cmd.Chat, msg); | |
} | |
_IrcClient.SendMessage(SendType.Message, nickname, message); | |
} else { | |
_Say(chat, message); | |
} | |
} | |
private IList<string> SplitMessage(string command, string target, string message) | |
{ | |
List<string> messages = new List<string>(); | |
int line = 0; | |
do { | |
var lineByteLength = GetProtocolMessageLength(command, target, message); | |
if (lineByteLength <= 512) { | |
if (line > 0) { | |
// remove leading spaces as we are a new line | |
messages.Add(message.TrimStart(new char[] {' '})); | |
} else { | |
messages.Add(message); | |
} | |
break; | |
} | |
line++; | |
// UTF8 can have multi-byte chars, thus we need to remove char | |
// by char and see when it fits into an IRC message | |
var chunkBuilder = new StringBuilder(message); | |
var chunkByteLength = lineByteLength; | |
while (chunkByteLength > 512) { | |
chunkBuilder.Length--; | |
chunkByteLength = GetProtocolMessageLength( | |
command, target, chunkBuilder.ToString() | |
); | |
} | |
var chunk = chunkBuilder.ToString(); | |
string nextChar = message.Substring(chunk.Length, 1); | |
if (nextChar != " ") { | |
// we split in the middle of a word, split it better! | |
int lastWordPos = chunk.LastIndexOf(" "); | |
if (lastWordPos > 0) { | |
// the chunk has to get smaller, else we run into an | |
// endless loop | |
chunk = chunk.Substring(0, lastWordPos); | |
} | |
} | |
// remove leading spaces as we are a new line | |
messages.Add(chunk.TrimStart(new char[] {' '})); | |
message = message.Substring(chunk.Length); | |
} while (true); | |
return messages; | |
} | |
public void CommandAllMessage(CommandModel cd) | |
{ | |
if (cd.DataArray.Length < 2) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
string message = cd.Parameter; | |
foreach (ChatModel chat in Chats) { | |
if (chat.ChatType != ChatType.Group) { | |
// only show on group chats | |
continue; | |
} | |
CommandModel msgCmd = new CommandModel( | |
cd.FrontendManager, | |
cd.Chat, | |
String.Format("{0} {1}", chat.ID, message) | |
); | |
CommandMessageChannel(msgCmd); | |
} | |
} | |
public void CommandAllNotice(CommandModel cd) | |
{ | |
if (cd.DataArray.Length < 2) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
string message = cd.Parameter; | |
foreach (ChatModel chat in Chats) { | |
if (chat.ChatType != ChatType.Group) { | |
// only show on group chats | |
continue; | |
} | |
CommandModel msgCmd = new CommandModel( | |
cd.FrontendManager, | |
cd.Chat, | |
String.Format("{0} {1}", chat.ID, message) | |
); | |
CommandNotice(msgCmd); | |
} | |
} | |
public void CommandAllMe(CommandModel cd) | |
{ | |
if (cd.DataArray.Length < 2) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
string message = cd.Parameter; | |
foreach (ChatModel chat in Chats) { | |
if (chat.ChatType != ChatType.Group) { | |
// only show on group chats | |
continue; | |
} | |
CommandModel msgCmd = new CommandModel( | |
cd.FrontendManager, | |
chat, | |
message | |
); | |
CommandMe(msgCmd); | |
} | |
} | |
public void CommandPart(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
if ((cd.DataArray.Length >= 2) && | |
(cd.DataArray[1].Length >= 1)) { | |
// have to guess here if we got a channel passed or not | |
var chanType = cd.DataArray[1][0].ToString(); | |
if (ChannelTypes.Contains(chanType)) { | |
// seems to be a channel | |
string[] channels = cd.DataArray[1].Split(new char[] {','}); | |
string message = null; | |
if (cd.DataArray.Length >= 3) { | |
message = String.Join(" ", cd.DataArray, 2, cd.DataArray.Length-2); | |
} | |
foreach (string channel in channels) { | |
if (message != null) { | |
_IrcClient.RfcPart(channel, message); | |
} else { | |
_IrcClient.RfcPart(channel); | |
} | |
} | |
} else { | |
// sems to be only a part message | |
_IrcClient.RfcPart(chat.ID, cd.Parameter); | |
} | |
} else { | |
_IrcClient.RfcPart(chat.ID); | |
} | |
} | |
public void CommandAway(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 2) { | |
SetPresenceStatus(PresenceStatus.Away, cd.Parameter); | |
} else { | |
SetPresenceStatus(PresenceStatus.Online, null); | |
} | |
} | |
public void CommandOper(CommandModel cd) | |
{ | |
if (cd.DataArray.Length < 3) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
_IrcClient.RfcOper(cd.DataArray[1], cd.DataArray[2]); | |
} | |
public void CommandCtcp(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 3) { | |
string destination = cd.DataArray[1]; | |
string command = cd.DataArray[2].ToUpper(); | |
string parameters = String.Empty; | |
if (cd.DataArray.Length >= 4) { | |
parameters = String.Join(" ", cd.DataArray, 3, cd.DataArray.Length-3); | |
} | |
var builder = CreateMessageBuilder(); | |
builder.AppendText("[ctcp({0})] {1} {2}", destination, command, | |
parameters); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
_IrcClient.SendMessage(SendType.CtcpRequest, destination, command + " " + parameters); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandPing(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 2) { | |
string destination = cd.DataArray[1]; | |
string timestamp = DateTime.Now.ToFileTime().ToString(); | |
var builder = CreateMessageBuilder(); | |
builder.AppendText("[ctcp({0})] {1} {2}", destination, "PING", | |
timestamp); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
_IrcClient.SendMessage(SendType.CtcpRequest, destination, "PING " + timestamp); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandTime(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 2) { | |
string destination = cd.DataArray[1]; | |
var builder = CreateMessageBuilder(); | |
builder.AppendText("[ctcp({0})] {1}", destination, "TIME"); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
_IrcClient.SendMessage(SendType.CtcpRequest, destination, "TIME"); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandVersion(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 2) { | |
string destination = cd.DataArray[1]; | |
var builder = CreateMessageBuilder(); | |
builder.AppendText("[ctcp({0})] {1}", destination, "VERSION"); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
_IrcClient.SendMessage(SendType.CtcpRequest, destination, "VERSION"); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandFinger(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 2) { | |
string destination = cd.DataArray[1]; | |
var builder = CreateMessageBuilder(); | |
builder.AppendText("[ctcp({0})] {1}", destination, "FINGER"); | |
Session.AddMessageToChat(Chat, builder.ToMessage()); | |
_IrcClient.SendMessage(SendType.CtcpRequest, destination, "FINGER"); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandWho(CommandModel cd) | |
{ | |
if (cd.DataArray.Length < 2) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
IList<WhoInfo> infos = _IrcClient.GetWhoList(cd.DataArray[1]); | |
// irssi: * meebey H 1 ~meebey@e176002059.adsl.alicedsl.de [Mirco Bauer] | |
foreach (WhoInfo info in infos) { | |
string mode; | |
if (info.IsIrcOp) { | |
mode = _("IRC Op"); | |
} else if (info.IsOwner) { | |
mode = _("Owner"); | |
} else if (info.IsChannelAdmin) { | |
mode = _("ChanAdmin"); | |
} else if (info.IsOp) { | |
mode = _("Op"); | |
} else if (info.IsHalfop) { | |
mode = _("Halfop"); | |
} else if (info.IsVoice) { | |
mode = _("Voice"); | |
} else { | |
mode = String.Empty; | |
} | |
string msg = String.Format( | |
"-!- {0} {1} {2}{3} {4} {5}@{6} [{7}]", | |
info.Channel, | |
info.Nick, | |
mode, | |
info.IsAway ? " (" + _("away") + ")" : String.Empty, | |
info.HopCount, | |
info.Ident, | |
info.Host, | |
info.Realname | |
); | |
var builder = CreateMessageBuilder().AppendText(msg); | |
Session.AddMessageToChat(cd.Chat, builder.ToMessage()); | |
} | |
} | |
public void CommandWhoIs(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 2) { | |
_IrcClient.RfcWhois(cd.Parameter); | |
} else { | |
if (cd.Chat is PersonChatModel) { | |
var pchat = (PersonChatModel) cd.Chat; | |
_IrcClient.RfcWhois(pchat.Person.ID); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
} | |
public void CommandWhoWas(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 2) { | |
_IrcClient.RfcWhowas(cd.DataArray[1]); | |
} else { | |
if (cd.Chat is PersonChatModel) { | |
var pchat = (PersonChatModel) cd.Chat; | |
_IrcClient.RfcWhowas(pchat.Person.ID); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
} | |
public void CommandTopic(CommandModel cd) | |
{ | |
MessageBuilder builder; | |
string channel = cd.Chat.ID; | |
if (cd.DataArray.Length >= 2) { | |
_IrcClient.RfcTopic(channel, cd.Parameter); | |
} else { | |
var groupChat = (GroupChatModel) Session.GetChat( | |
channel, ChatType.Group, this | |
); | |
if (groupChat != null) { | |
var topic = groupChat.Topic; | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
if (topic != null && !topic.IsEmpty) { | |
builder.AppendFormat(_("Topic for {0}: {1}"), channel, topic); | |
} else { | |
builder.AppendText(_("No topic set for {0}"), channel); | |
} | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
} | |
} | |
public void CommandOp(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
string channel = chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.Op(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.Op(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandDeop(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
string channel = chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.Deop(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.Deop(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandVoice(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
string channel = chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.Voice(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.Voice(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandDevoice(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
string channel = chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.Devoice(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.Devoice(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandOwner(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
string channel = chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.Owner(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.Owner(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandDeowner(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
string channel = chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.Deowner(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.Deowner(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandChanAdmin(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
string channel = chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.ChanAdmin(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.ChanAdmin(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandDeChanAdmin(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
string channel = chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.DeChanAdmin(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.DeChanAdmin(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandHalfop(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
string channel = chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.Halfop(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.Halfop(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandDehalfop(CommandModel cd) | |
{ | |
ChatModel chat = cd.Chat; | |
string channel = chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.Dehalfop(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.Dehalfop(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandBan(CommandModel cd) | |
{ | |
MessageBuilder builder; | |
string channel = cd.Chat.ID; | |
if (cd.DataArray.Length == 2) { | |
// TODO: use a smart mask by default | |
_IrcClient.Ban(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.Ban(channel, candidates); | |
} else { | |
IList<BanInfo> infos = _IrcClient.GetBanList(channel); | |
int i = 1; | |
foreach (BanInfo info in infos) { | |
string msg = String.Format( | |
"{0} - {1}: {2} {3}", | |
i++, | |
info.Channel, | |
_("ban"), | |
info.Mask | |
); | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(msg); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
if (infos.Count == 0) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("No bans in channel"), channel); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
} | |
} | |
public void CommandUnban(CommandModel cd) | |
{ | |
string channel = cd.Chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.Unban(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.Unban(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandBanException(CommandModel cd) | |
{ | |
MessageBuilder builder; | |
string channel = cd.Chat.ID; | |
if (cd.DataArray.Length == 2) { | |
// TODO: use a smart mask by default | |
_IrcClient.BanException(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.BanException(channel, candidates); | |
} else { | |
IList<BanInfo> infos = _IrcClient.GetBanExceptionList(channel); | |
int i = 1; | |
foreach (BanInfo info in infos) { | |
string msg = String.Format( | |
"{0} - {1}: {2} {3}", | |
i++, | |
info.Channel, | |
_("ban exception"), | |
info.Mask | |
); | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(msg); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
if (infos.Count == 0) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("No ban exceptions in channel"), channel); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
} | |
} | |
public void CommandUnBanException(CommandModel cd) | |
{ | |
string channel = cd.Chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.UnBanException(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.UnBanException(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandInviteException(CommandModel cd) | |
{ | |
MessageBuilder builder; | |
string channel = cd.Chat.ID; | |
if (cd.DataArray.Length == 2) { | |
// TODO: use a smart mask by default | |
_IrcClient.InviteException(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.InviteException(channel, candidates); | |
} else { | |
IList<BanInfo> infos = _IrcClient.GetInviteExceptionList(channel); | |
int i = 1; | |
foreach (BanInfo info in infos) { | |
string msg = String.Format( | |
"{0} - {1}: {2} {3}", | |
i++, | |
info.Channel, | |
_("invite exception"), | |
info.Mask | |
); | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(msg); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
if (infos.Count == 0) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("No invite exceptions in channel"), channel); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
} | |
} | |
public void CommandUnInviteException(CommandModel cd) | |
{ | |
string channel = cd.Chat.ID; | |
if (cd.DataArray.Length == 2) { | |
_IrcClient.UnInviteException(channel, cd.Parameter); | |
} else if (cd.DataArray.Length > 2) { | |
string[] candidates = cd.Parameter.TrimEnd().Split(new char[] {' '}); | |
_IrcClient.UnInviteException(channel, candidates); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandKick(CommandModel cd) | |
{ | |
string channel = cd.Chat.ID; | |
if (cd.DataArray.Length >= 2) { | |
string[] candidates = cd.DataArray[1].Split(new char[] {','}); | |
if (cd.DataArray.Length >= 3) { | |
string reason = String.Join(" ", cd.DataArray, 2, cd.DataArray.Length-2); | |
foreach (string nick in candidates) { | |
_IrcClient.RfcKick(channel, nick, reason); | |
} | |
} else { | |
foreach (string nick in candidates) { | |
_IrcClient.RfcKick(channel, nick); | |
} | |
} | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
public void CommandKickban(CommandModel cd) | |
{ | |
if (cd.DataArray.Length < 2) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
var chat = cd.Chat as GroupChatModel; | |
if (chat == null) { | |
return; | |
} | |
string channel = chat.ID; | |
string[] candidates = cd.DataArray[1].Split(new char[] {','}); | |
string reason = null; | |
if (cd.DataArray.Length >= 3) { | |
reason = String.Join(" ", cd.DataArray, 2, cd.DataArray.Length-2); | |
} | |
foreach (string nick in candidates) { | |
var victim = (IrcGroupPersonModel) chat.GetPerson(nick); | |
if (victim == null) { | |
continue; | |
} | |
_IrcClient.Ban(channel, "*!*" + victim.Ident + "@" + victim.Host); | |
if (reason == null) { | |
_IrcClient.RfcKick(channel, victim.NickName); | |
} else { | |
_IrcClient.RfcKick(channel, victim.NickName, reason); | |
} | |
} | |
} | |
public void CommandMode(CommandModel cd) | |
{ | |
string target = null; | |
string mode = null; | |
if (cd.DataArray.Length >= 2) { | |
// /mode #smuxi | |
// /mode meebey | |
var param1 = cd.DataArray[1]; | |
if (param1.StartsWith("+") || param1.StartsWith("-")) { | |
// no target given, this is the mode already | |
// /mode +i (on server) | |
// /mode +b (on channel) | |
// /mode +b *!*@foo (on channel) | |
if (cd.Chat.ChatType == ChatType.Group) { | |
target = cd.Chat.ID; | |
} else { | |
target = _IrcClient.Nickname; | |
} | |
mode = cd.Parameter; | |
} else { | |
target = param1; | |
if (cd.DataArray.Length >= 3) { | |
// /mode #smuxi +b *!*@foo | |
// /mode #smuxi +b | |
// /mode meebey +i | |
mode = String.Join(" ", cd.DataArray, 2, cd.DataArray.Length-2); | |
} | |
} | |
} else { | |
// /mode (on server) | |
// /mode (on channel) | |
if (cd.Chat.ChatType == ChatType.Group) { | |
target = cd.Chat.ID; | |
} else { | |
target = _IrcClient.Nickname; | |
} | |
} | |
if (target != null && mode != null) { | |
_IrcClient.RfcMode(target, mode); | |
} else if (target != null) { | |
if (_IrcClient.IsMe(target)) { | |
var builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(_("Your user mode is {0}"), | |
String.Format("[{0}]", _IrcClient.Usermode)); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
} else { | |
_IrcClient.RfcMode(target); | |
} | |
} | |
} | |
public void CommandInvite(CommandModel cd) | |
{ | |
string channel; | |
if (cd.DataArray.Length >= 3) { | |
channel = cd.DataArray[2]; | |
} else { | |
channel = cd.Chat.ID; | |
} | |
if (cd.DataArray.Length < 2) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
var invitee = cd.DataArray[1]; | |
var groupChat = cd.Chat as GroupChatModel; | |
if (groupChat != null && groupChat.GetPerson(invitee) != null) { | |
var msg = CreateMessageBuilder(). | |
AppendEventPrefix(). | |
AppendText(_("{0} is already on {1}"), | |
invitee, channel). | |
ToMessage(); | |
Session.AddMessageToFrontend(cd, msg); | |
} else { | |
_IrcClient.RfcInvite(invitee, channel); | |
var msg = CreateMessageBuilder(). | |
AppendEventPrefix(). | |
AppendText(_("Inviting {0} to {1}"), | |
invitee, channel). | |
ToMessage(); | |
Session.AddMessageToFrontend(cd, msg); | |
} | |
} | |
public void CommandNames(CommandModel cd) | |
{ | |
/* | |
13:10 [Users #smuxi] | |
13:10 [ CIA-5] [ d-best] [ meebey] [ meebey_] [ NotZh817] [ RAOF] | |
13:10 -!- Irssi: #smuxi: Total of 6 nicks [0 ops, 0 halfops, 0 voices, 6 normal] | |
*/ | |
ChatModel chat = cd.Chat; | |
if (!(chat is GroupChatModel)) { | |
return; | |
} | |
GroupChatModel groupChat = (GroupChatModel) chat; | |
var builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText("[{0} {1}]", _("Users"), groupChat.Name); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
builder = CreateMessageBuilder(); | |
int opCount = 0; | |
int voiceCount = 0; | |
int normalCount = 0; | |
builder.AppendEventPrefix(); | |
// sort nicklist | |
var persons = groupChat.Persons; | |
if (persons == null) { | |
persons = new Dictionary<string, PersonModel>(0); | |
} | |
List<PersonModel> ircPersons = new List<PersonModel>(persons.Values); | |
ircPersons.Sort((a, b) => (a.IdentityName.CompareTo(b.IdentityName))); | |
builder.AppendText("[ "); | |
foreach (IrcGroupPersonModel ircPerson in ircPersons) { | |
string mode; | |
if (ircPerson.IsOwner) { | |
opCount++; | |
mode = "~"; | |
} else if (ircPerson.IsChannelAdmin) { | |
opCount++; | |
mode = "&"; | |
} else if (ircPerson.IsOp) { | |
opCount++; | |
mode = "@"; | |
} else if (ircPerson.IsHalfop) { | |
opCount++; | |
mode = "%"; | |
} else if (ircPerson.IsVoice) { | |
voiceCount++; | |
mode = "+"; | |
} else { | |
normalCount++; | |
mode = String.Empty; | |
} | |
if (!String.IsNullOrEmpty(mode)) { | |
builder.AppendText(mode); | |
} | |
builder.AppendNick(ircPerson); | |
builder.AppendSpace(); | |
} | |
builder.AppendText("]"); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText( | |
String.Format( | |
_("Total of {0} users [{1} ops, {2} voices, {3} normal]"), | |
opCount + voiceCount + normalCount, | |
opCount, | |
voiceCount, | |
normalCount | |
) | |
); | |
Session.AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
public void CommandRaw(CommandModel cd) | |
{ | |
if (_IrcClient.IsRegistered) { | |
_IrcClient.WriteLine(cd.Parameter); | |
} else { | |
_IrcClient.WriteLine(cd.Parameter, Priority.Critical); | |
} | |
} | |
public void CommandMe(CommandModel cd) | |
{ | |
if (cd.DataArray.Length < 2) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
_IrcClient.SendMessage(SendType.Action, cd.Chat.ID, cd.Parameter); | |
var builder = CreateMessageBuilder(); | |
builder.AppendActionPrefix(); | |
builder.AppendIdendityName(Me); | |
builder.AppendText(" "); | |
builder.AppendMessage(cd.Parameter); | |
Session.AddMessageToChat(cd.Chat, builder.ToMessage(), true); | |
} | |
public void CommandNotice(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 3) { | |
string target = cd.DataArray[1]; | |
string message = String.Join(" ", cd.DataArray, 2, cd.DataArray.Length-2); | |
_IrcClient.SendMessage(SendType.Notice, target, message); | |
var chat = GetChat(target, ChatType.Group); | |
if (chat == null) { | |
// wasn't a channel but maybe a query | |
chat = GetChat(target, ChatType.Person); | |
} | |
if (chat == null) { | |
chat = _NetworkChat; | |
} | |
var msg = CreateMessageBuilder(). | |
AppendText("[notice({0})] ", target). | |
AppendMessage(message). | |
ToMessage(); | |
Session.AddMessageToChat(chat, msg, true); | |
} | |
} | |
public void CommandNick(CommandModel cd) | |
{ | |
if (cd.DataArray.Length >= 2) { | |
_IrcClient.RfcNick(cd.Parameter); | |
} | |
} | |
public void CommandQuit(CommandModel cd) | |
{ | |
Trace.Call(cd); | |
string message = cd.Parameter; | |
// else SmartIrc4net would reconnect us | |
_IrcClient.AutoReconnect = false; | |
// else the Listen() thread would try to connect again | |
_Listening = false; | |
// when we are disconnected, remove all chats | |
_IrcClient.OnDisconnected += delegate { | |
// cleanup all open chats | |
Dispose(); | |
}; | |
// ok now we are ready to die | |
if (message != null) { | |
_IrcClient.RfcQuit(message); | |
} else { | |
_IrcClient.RfcQuit(); | |
} | |
} | |
private void _Run() | |
{ | |
Trace.Call(); | |
try { | |
Connect(_FrontendManager); | |
while (_Listening) { | |
try { | |
_Listen(); | |
#if LOG4NET | |
_Logger.Warn("_Run(): _Listen() returned."); | |
#endif | |
} catch (ThreadAbortException) { | |
throw; | |
} catch (Exception ex) { | |
#if LOG4NET | |
_Logger.Error("_Run(): exception in _Listen() occurred!" ,ex); | |
#endif | |
Reconnect(_FrontendManager); | |
} | |
// sleep for 10 seconds, we don't want to be abusive | |
System.Threading.Thread.Sleep(10000); | |
} | |
} catch (ThreadAbortException) { | |
#if LOG4NET | |
_Logger.Debug("_Run(): thread aborted"); | |
#endif | |
} catch (Exception ex) { | |
#if LOG4NET | |
_Logger.Error(ex); | |
#endif | |
} | |
// don't need the FrontendManager anymore | |
_FrontendManager = null; | |
} | |
private void _Listen() | |
{ | |
try { | |
_IrcClient.Listen(); | |
} catch (Exception ex) { | |
var msg = CreateMessageBuilder(). | |
AppendEventPrefix(). | |
AppendText(_("Connection error! Reason: ")). | |
AppendText(ex.Message). | |
ToMessage(); | |
Session.AddMessageToChat(Chat, msg); | |
throw; | |
} | |
} | |
private void _NotEnoughParameters(CommandModel cd) | |
{ | |
var msg = CreateMessageBuilder(). | |
AppendEventPrefix(). | |
AppendText(_("Not enough parameters for {0} command"), cd.Command). | |
ToMessage(); | |
Session.AddMessageToFrontend(cd, msg); | |
} | |
private void _NotConnected(CommandModel cd) | |
{ | |
var msg = CreateMessageBuilder(). | |
AppendEventPrefix(). | |
AppendText(_("Not connected to server")). | |
ToMessage(); | |
Session.AddMessageToFrontend(cd, msg); | |
} | |
private void ApplyConfig(UserConfig config, ServerModel server) | |
{ | |
_Host = server.Hostname; | |
_Port = server.Port; | |
if (String.IsNullOrEmpty(server.Network)) { | |
_Network = server.Hostname; | |
} else { | |
_Network = server.Network; | |
} | |
if (String.IsNullOrEmpty(server.Nickname)) { | |
_Nicknames = (string[]) config["Connection/Nicknames"]; | |
} else { | |
_Nicknames = server.Nickname.Split(' '); | |
} | |
if (String.IsNullOrEmpty(server.Realname)) { | |
_Realname = (string) config["Connection/Realname"]; | |
} else { | |
_Realname = server.Realname; | |
} | |
if (String.IsNullOrEmpty(server.Username)) { | |
_Username = (string) config["Connection/Username"]; | |
} else { | |
_Username = server.Username; | |
} | |
_Password = server.Password; | |
// internal fallbacks | |
if (String.IsNullOrEmpty(_Username)) { | |
_Username = "smuxi"; | |
} | |
// IRC specific settings | |
if (server is IrcServerModel) { | |
var ircServer = (IrcServerModel) server; | |
if (ircServer.Nicknames != null && ircServer.Nicknames.Count > 0) { | |
_Nicknames = ircServer.Nicknames.ToArray(); | |
} | |
} | |
// add fallbacks if only one nick was specified, else we get random | |
// number nicks when nick collisions happen | |
if (_Nicknames.Length == 1) { | |
_Nicknames = new string[] { _Nicknames[0], _Nicknames[0] + "_", _Nicknames[0] + "__" }; | |
} | |
string encodingName = (string) config["Connection/Encoding"]; | |
if (String.IsNullOrEmpty(encodingName)) { | |
_IrcClient.Encoding = Encoding.Default; | |
} else { | |
try { | |
_IrcClient.Encoding = Encoding.GetEncoding(encodingName); | |
} catch (Exception ex) { | |
#if LOG4NET | |
_Logger.Warn("ApplyConfig(): Error getting encoding for: " + | |
encodingName + " falling back to system encoding.", ex); | |
#endif | |
_IrcClient.Encoding = Encoding.Default; | |
} | |
} | |