Permalink
Cannot retrieve contributors at this time
/* | |
* Smuxi - Smart MUltipleXed Irc | |
* | |
* Copyright (c) 2005-2015 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.Net; | |
using System.Linq; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Runtime.Remoting; | |
using System.Threading; | |
using Smuxi.Common; | |
namespace Smuxi.Engine | |
{ | |
public class Session : PermanentRemoteObject, IFrontendUI | |
{ | |
#if LOG4NET | |
private static readonly log4net.ILog f_Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | |
#endif | |
private static readonly string _LibraryTextDomain = "smuxi-engine"; | |
public static CertificateValidator CertificateValidator { get; private set; } | |
private int _Version = 0; | |
private IDictionary<string, FrontendManager> _FrontendManagers; | |
private IList<IProtocolManager> _ProtocolManagers; | |
private List<ChatModel> _Chats; | |
private SessionChatModel _SessionChat; | |
private Config _Config; | |
private string _Username; | |
private ProtocolManagerFactory _ProtocolManagerFactory; | |
private UserConfig _UserConfig; | |
private FilterListController _FilterListController; | |
private ICollection<FilterModel> _Filters; | |
Timer NewsFeedTimer { get; set; } | |
List<string> SeenNewsFeedIds { get; set; } | |
DateTime NewsFeedLastModified { get; set; } | |
TimeSpan NewsFeedUpdateInterval { get; set; } | |
TimeSpan NewsFeedRetryInterval { get; set; } | |
bool IsFirstNewsFeedEntry { get; set; } | |
internal MessageBuilderSettings MessageBuilderSettings { get; private set; } | |
public event EventHandler<GroupChatPersonAddedEventArgs> GroupChatPersonAdded; | |
public event EventHandler<GroupChatPersonRemovedEventArgs> GroupChatPersonRemoved; | |
public event EventHandler<GroupChatPersonUpdatedEventArgs> GroupChatPersonUpdated; | |
public event EventHandler<EventMessageEventArgs> EventMessage; | |
public IList<IProtocolManager> ProtocolManagers { | |
get { | |
return _ProtocolManagers; | |
} | |
} | |
public IProtocolManager FirstProtocolManager { | |
get { | |
lock (_ProtocolManagers) { | |
if (_ProtocolManagers.Count == 0) { | |
return null; | |
} else { | |
return _ProtocolManagers[0]; | |
} | |
} | |
} | |
} | |
public IList<ChatModel> Chats { | |
get { | |
return _Chats; | |
} | |
} | |
public SessionChatModel SessionChat { | |
get { | |
return _SessionChat; | |
} | |
} | |
public int Version { | |
get { | |
return _Version; | |
} | |
} | |
public Config Config { | |
get { | |
return _Config; | |
} | |
} | |
public UserConfig UserConfig { | |
get { | |
return _UserConfig; | |
} | |
} | |
public string Username { | |
get { | |
return _Username; | |
} | |
} | |
public bool IsLocal { | |
get { | |
return _Username == "local"; | |
} | |
} | |
internal ICollection<FilterModel> Filters { | |
get { | |
return _Filters; | |
} | |
set { | |
_Filters = value; | |
} | |
} | |
static Session() | |
{ | |
CertificateValidator = new CertificateValidator(); | |
} | |
public Session(Config config, ProtocolManagerFactory protocolManagerFactory, | |
string username) | |
{ | |
Trace.Call(config, protocolManagerFactory, username); | |
if (config == null) { | |
throw new ArgumentNullException("config"); | |
} | |
if (protocolManagerFactory == null) { | |
throw new ArgumentNullException("protocolManagerFactory"); | |
} | |
if (username == null) { | |
throw new ArgumentNullException("username"); | |
} | |
_Config = config; | |
_ProtocolManagerFactory = protocolManagerFactory; | |
_Username = username; | |
_FrontendManagers = new Dictionary<string, FrontendManager>(); | |
_ProtocolManagers = new List<IProtocolManager>(); | |
_UserConfig = new UserConfig(config, username); | |
_UserConfig.Changed += OnUserConfigChanged; | |
_FilterListController = new FilterListController(_UserConfig); | |
_Filters = _FilterListController.GetFilterList().Values; | |
MessageBuilderSettings = new MessageBuilderSettings(); | |
MessageBuilderSettings.ApplyConfig(_UserConfig); | |
_Chats = new List<ChatModel>(); | |
InitSessionChat(); | |
SeenNewsFeedIds = new List<string>(); | |
NewsFeedUpdateInterval = TimeSpan.FromHours(12); | |
NewsFeedRetryInterval = TimeSpan.FromMinutes(5); | |
IsFirstNewsFeedEntry = true; | |
NewsFeedTimer = new Timer(delegate { UpdateNewsFeed(); }, null, | |
TimeSpan.Zero, NewsFeedUpdateInterval); | |
} | |
public void ExecuteOnStartupCommands() | |
{ | |
foreach (string command in (string[]) _UserConfig["OnStartupCommands"]) { | |
if (command.Length == 0) { | |
continue; | |
} | |
var cd = new CommandModel(null, _SessionChat, | |
(string)_UserConfig["Interface/Entry/CommandCharacter"], | |
command); | |
var handled = Command(cd); | |
if (!handled) { | |
#if LOG4NET | |
f_Logger.Error("ExecuteOnStartupCommands(): command unknown: " + command); | |
#endif | |
var builder = CreateMessageBuilder(). | |
AppendEventPrefix(). | |
AppendErrorText("unknown command: {0}", command); | |
AddMessageToChat(_SessionChat, builder.ToMessage(), true); | |
} | |
} | |
} | |
public void ProcessAutoConnect() | |
{ | |
var serverCon = new ServerListController(_UserConfig); | |
IList<ServerModel> servers = serverCon.GetServerList(); | |
foreach (ServerModel server in servers) { | |
if (!server.OnStartupConnect) { | |
continue; | |
} | |
var msg = String.Format( | |
_("Automatically connecting to {0}..."), | |
String.Format("{0}/{1} ({2}:{3})", server.Protocol, | |
server.Network, server.Hostname, server.Port) | |
); | |
#if LOG4NET | |
f_Logger.Info(msg); | |
#endif | |
var builder = CreateMessageBuilder(). | |
AppendEventPrefix(). | |
AppendText(msg); | |
AddMessageToChat(_SessionChat, builder.ToMessage(), true); | |
var srv = server; | |
// run connects in background threads as they block | |
ThreadPool.QueueUserWorkItem(delegate { | |
bool isError = false; | |
try { | |
IProtocolManager protocolManager = Connect(srv, null); | |
// if the connect command was correct, we should be | |
// able to get the chat model | |
if (protocolManager.Chat == null) { | |
isError = true; | |
} | |
} catch (Exception ex) { | |
#if LOG4NET | |
f_Logger.Error("ProcessAutoConnect(): Exception during "+ | |
"automatic connect: ", ex); | |
#endif | |
isError = true; | |
} | |
if (isError) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendErrorText( | |
_("Automatic connect to {0} failed!"), | |
srv.Hostname + ":" + srv.Port | |
); | |
AddMessageToChat(_SessionChat, builder.ToMessage(), true); | |
} | |
}); | |
} | |
} | |
public IProtocolManager NextProtocolManager(IProtocolManager currentProtocolManager) | |
{ | |
lock (_ProtocolManagers) { | |
if (_ProtocolManagers.Count == 0) { | |
return null; | |
} | |
int pos = 0; | |
if (currentProtocolManager != null) { | |
pos = _ProtocolManagers.IndexOf(currentProtocolManager); | |
} | |
if (pos < _ProtocolManagers.Count - 1) { | |
pos++; | |
} else { | |
pos = 0; | |
} | |
return _ProtocolManagers[pos]; | |
} | |
} | |
protected MessageBuilder CreateMessageBuilder() | |
{ | |
var builder = new MessageBuilder(); | |
// copy settings so the caller can override settings without | |
// changing the settings of the complete session | |
builder.Settings = new MessageBuilderSettings(MessageBuilderSettings); | |
return builder; | |
} | |
public void RegisterFrontendUI(IFrontendUI ui) | |
{ | |
Trace.Call(ui); | |
if (ui == null) { | |
throw new ArgumentNullException("ui"); | |
} | |
string uri = GetUri(ui); | |
#if LOG4NET | |
f_Logger.Debug("Registering UI with URI: " + uri); | |
#endif | |
// add the FrontendManager to the hashtable with an unique .NET remoting identifier | |
FrontendManager fm = new FrontendManager(this, ui); | |
lock (_FrontendManagers) { | |
_FrontendManagers[uri] = fm; | |
} | |
} | |
internal void DeregisterFrontendManager(FrontendManager fm) | |
{ | |
Trace.Call(fm); | |
if (fm == null) { | |
throw new ArgumentNullException("fm"); | |
} | |
string key = null; | |
lock (_FrontendManagers) { | |
foreach (KeyValuePair<string, FrontendManager> kv in _FrontendManagers) { | |
if (kv.Value == fm) { | |
key = kv.Key; | |
break; | |
} | |
} | |
} | |
if (key == null) { | |
#if LOG4NET | |
f_Logger.Debug("DeregisterFrontendManager(fm): could not find " + | |
"frontend manager (probably already cleanly " + | |
" deregistered), ignoring..."); | |
#endif | |
return; | |
} | |
lock (_FrontendManagers) { | |
_FrontendManagers.Remove(key); | |
} | |
#if LOG4NET | |
f_Logger.Debug("DeregisterFrontendUI(fm): disposing FrontendManager"); | |
#endif | |
fm.Dispose(); | |
CheckPresenceStatus(); | |
} | |
public void DeregisterFrontendUI(IFrontendUI ui) | |
{ | |
Trace.Call(ui); | |
if (ui == null) { | |
throw new ArgumentNullException("ui"); | |
} | |
string uri = GetUri(ui); | |
#if LOG4NET | |
f_Logger.Debug("DeregisterFrontendUI(ui): deregistering UI with URI: "+uri); | |
#endif | |
FrontendManager manager; | |
lock (_FrontendManagers) { | |
_FrontendManagers.TryGetValue(uri, out manager); | |
_FrontendManagers.Remove(uri); | |
} | |
if (manager == null) { | |
#if LOG4NET | |
f_Logger.Error("DeregisterFrontendUI(ui): can't dispose as FrontendManager not found with URI: " + uri); | |
#endif | |
} else { | |
#if LOG4NET | |
f_Logger.Debug("DeregisterFrontendUI(ui): disposing FrontendManager with URI: " + uri); | |
#endif | |
manager.Dispose(); | |
} | |
CheckPresenceStatus(); | |
} | |
public FrontendManager GetFrontendManager(IFrontendUI ui) | |
{ | |
Trace.Call(ui); | |
if (ui == null) { | |
throw new ArgumentNullException("ui"); | |
} | |
lock (_FrontendManagers) { | |
return _FrontendManagers[GetUri(ui)]; | |
} | |
} | |
private string GetUri(IFrontendUI ui) | |
{ | |
if (ui == null) { | |
throw new ArgumentNullException("ui"); | |
} | |
if (IsLocal) { | |
return "local"; | |
} | |
return RemotingServices.GetObjectUri((MarshalByRefObject)ui); | |
} | |
public static bool IsLocalFrontend(IFrontendUI ui) | |
{ | |
if (ui == null) { | |
throw new ArgumentNullException("ui"); | |
} | |
return RemotingServices.GetObjectUri((MarshalByRefObject)ui) == null; | |
} | |
public ChatModel GetChat(string id, ChatType chatType, IProtocolManager networkManager) | |
{ | |
if (id == null) { | |
throw new ArgumentNullException("id"); | |
} | |
lock (_Chats) { | |
foreach (ChatModel chat in _Chats) { | |
if ((chat.ID.ToLower() == id.ToLower()) && | |
(chat.ChatType == chatType) && | |
(chat.ProtocolManager == networkManager)) { | |
return chat; | |
} | |
} | |
} | |
return null; | |
} | |
public bool Command(CommandModel cd) | |
{ | |
Trace.Call(cd); | |
if (cd == null) { | |
throw new ArgumentNullException("cd"); | |
} | |
bool handled = false; | |
if (cd.IsCommand) { | |
switch (cd.Command) { | |
case "help": | |
CommandHelp(cd); | |
if (cd.Chat.ChatType == ChatType.Session) { | |
handled = true; | |
} | |
break; | |
case "server": | |
case "connect": | |
CommandConnect(cd); | |
handled = true; | |
break; | |
case "disconnect": | |
CommandDisconnect(cd); | |
handled = true; | |
break; | |
case "reconnect": | |
CommandReconnect(cd); | |
handled = true; | |
break; | |
case "network": | |
CommandNetwork(cd); | |
handled = true; | |
break; | |
case "config": | |
CommandConfig(cd); | |
handled = true; | |
break; | |
case "shutdown": | |
CommandShutdown(cd); | |
handled = true; | |
break; | |
case "echo": | |
CommandEcho(cd); | |
handled = true; | |
break; | |
default: | |
var filteredCmd = IOSecurity.GetFilteredPath(cd.Command); | |
var hooks = new HookRunner("engine", "session", | |
"command-" + filteredCmd); | |
var pm = cd.Chat.ProtocolManager; | |
hooks.Environments.Add(new CommandHookEnvironment(cd)); | |
hooks.Environments.Add(new ChatHookEnvironment(cd.Chat)); | |
if (pm != null) { | |
hooks.Environments.Add(new ProtocolManagerHookEnvironment(pm)); | |
} | |
var cmdChar = (string) UserConfig["Interface/Entry/CommandCharacter"]; | |
hooks.Commands.Add(new SessionHookCommand(this, cd.Chat, cmdChar)); | |
if (pm != null) { | |
hooks.Commands.Add(new ProtocolManagerHookCommand(pm, cd.Chat, cmdChar)); | |
} | |
// show time | |
hooks.Init(); | |
if (hooks.HasHooks) { | |
hooks.Run(); | |
handled = true; | |
} | |
break; | |
} | |
} else { | |
// normal text | |
if (cd.Chat.ChatType == ChatType.Session && | |
cd.FrontendManager != null && | |
cd.FrontendManager.CurrentProtocolManager == null) { | |
_NotConnected(cd); | |
handled = true; | |
} | |
} | |
return handled; | |
} | |
public void CommandHelp(CommandModel cd) | |
{ | |
Trace.Call(cd); | |
if (cd == null) { | |
throw new ArgumentNullException("cd"); | |
} | |
var builder = CreateMessageBuilder(); | |
// TRANSLATOR: this line is used as a label / category for a | |
// list of commands below | |
builder.AppendHeader(_("Engine Commands")); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
string[] help = { | |
"connect/server protocol [protocol-parameters]", | |
"connect/server network", | |
"disconnect [server]", | |
"network list", | |
"network close [network]", | |
"network switch [network]", | |
"config (save|load|list)", | |
"config get key", | |
"config set key=value", | |
"config remove key", | |
"shutdown" | |
}; | |
foreach (string line in help) { | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText(line); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
} | |
public void CommandConnect(CommandModel cd) | |
{ | |
Trace.Call(cd); | |
if (cd == null) { | |
throw new ArgumentNullException("cd"); | |
} | |
FrontendManager fm = cd.FrontendManager; | |
MessageBuilder builder = null; | |
string protocol = null; | |
ServerModel server = null; | |
// first lookup by network name | |
if (cd.DataArray.Length == 2) { | |
var network = cd.Parameter; | |
var serverSettings = new ServerListController(UserConfig); | |
server = serverSettings.GetServerByNetwork(network); | |
if (server == null) { | |
// HACK: simply assume the user meant irc if not specified | |
// as Smuxi is still primarly an IRC client | |
protocol = "irc"; | |
string cmd = String.Format("{0}connect irc {1}", | |
cd.CommandCharacter, cd.Parameter); | |
cd = new CommandModel(fm, cd.Chat, cd.CommandCharacter, cmd); | |
} | |
} else if (cd.DataArray.Length >= 3) { | |
protocol = cd.DataArray[1]; | |
} else { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
IProtocolManager protocolManager = null; | |
// TODO: detect matching protocol managers, how to parse host and port | |
// though in a protocol neutral ? | |
/* | |
foreach (IProtocolManager nm in _ProtocolManagers) { | |
if (nm.Host == server && | |
nm.Port == port) { | |
// reuse network manager | |
if (nm.IsConnected) { | |
fm.AddTextToCurrentChat("-!- " + String.Format( | |
_("Already connected to: {0}:{1}"), server, port)); | |
return; | |
} | |
networkManager = nm; | |
break; | |
} | |
} | |
*/ | |
if (protocolManager == null && server == null) { | |
try { | |
protocolManager = CreateProtocolManager(protocol); | |
lock (_ProtocolManagers) { | |
_ProtocolManagers.Add(protocolManager); | |
} | |
} catch (ArgumentException ex) { | |
if (ex.ParamName != "protocol") { | |
throw; | |
} | |
// this is an unknown protocol error | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendErrorText(ex.Message); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
return; | |
} | |
} | |
// HACK: this is hacky as the Command parser of the protocol manager | |
// will pass this command to it's connect method only if cd was | |
// constructed correctly beginning with /connect | |
// So make sure it's like it needs to be! | |
if (cd.Command != "connect") { | |
string cmd = String.Format("{0}connect {1}", | |
cd.CommandCharacter, | |
String.Join(" ", cd.DataArray, 1, | |
cd.DataArray.Length - 1)); | |
cd = new CommandModel(fm, cd.Chat, cd.CommandCharacter, cmd); | |
} | |
// run in background so it can't block the command queue | |
ThreadPool.QueueUserWorkItem(delegate { | |
try { | |
if (protocolManager == null && server != null) { | |
protocolManager = Connect(server, fm); | |
} else { | |
protocolManager.Command(cd); | |
} | |
// set this as current protocol manager | |
// but only if there was none set (we might be on a chat for example) | |
// or if this is the neutral "smuxi" tab | |
if (fm.CurrentProtocolManager == null || | |
(fm.CurrentChat != null && fm.CurrentChat.ChatType == ChatType.Session)) { | |
fm.CurrentProtocolManager = protocolManager; | |
fm.UpdateNetworkStatus(); | |
} | |
} catch (Exception ex) { | |
#if LOG4NET | |
f_Logger.Error("CommandConnect(): ", ex); | |
#endif | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendErrorText(_("Connect failed!")); | |
fm.AddMessageToChat(cd.Chat, builder.ToMessage()); | |
} | |
}); | |
} | |
public void CommandDisconnect(CommandModel cd) | |
{ | |
Trace.Call(cd); | |
if (cd == null) { | |
throw new ArgumentNullException("cd"); | |
} | |
FrontendManager fm = cd.FrontendManager; | |
IProtocolManager victim = null; | |
if (cd.DataArray.Length >= 2) { | |
string server = cd.DataArray[1]; | |
victim = GetProtocolManagerByHost(server); | |
if (victim == null) { | |
var builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendErrorText( | |
_("Disconnect failed - could not find server: {0}"), | |
server | |
); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
return; | |
} | |
} else { | |
victim = cd.Chat.ProtocolManager; | |
} | |
if (victim == null) { | |
return; | |
} | |
victim.Disconnect(fm); | |
victim.Dispose(); | |
lock (_ProtocolManagers) { | |
_ProtocolManagers.Remove(victim); | |
} | |
} | |
public void CommandReconnect(CommandModel cd) | |
{ | |
Trace.Call(cd); | |
if (cd == null) { | |
throw new ArgumentNullException("cd"); | |
} | |
var pm = cd.Chat.ProtocolManager; | |
if (pm == null) { | |
return; | |
} | |
ThreadPool.QueueUserWorkItem(delegate { | |
try { | |
pm.Reconnect(cd.FrontendManager); | |
} catch (Exception ex) { | |
#if LOG4NET | |
f_Logger.Error("CommandReconnect(): ", ex); | |
#endif | |
var builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendErrorText(_("Reconnect failed!")); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
}); | |
} | |
public void CommandConfig(CommandModel cd) | |
{ | |
Trace.Call(cd); | |
if (cd == null) { | |
throw new ArgumentNullException("cd"); | |
} | |
if (cd.DataArray.Length < 2) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
var builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
var action = cd.DataArray[1].ToLower(); | |
switch (action) { | |
case "load": | |
_Config.Load(); | |
builder.AppendText(_("Configuration reloaded")); | |
break; | |
case "save": | |
_Config.Save(); | |
builder.AppendText(_("Configuration saved")); | |
break; | |
case "get": | |
case "list": | |
string key = null; | |
if (action == "get" && cd.DataArray.Length >= 3) { | |
key = cd.DataArray[2]; | |
} | |
foreach (var entry in _UserConfig.OrderBy(kvp => kvp.Key)) { | |
if (key != null && | |
entry.Key.IndexOf(key, StringComparison.InvariantCultureIgnoreCase) == -1) { | |
continue; | |
} | |
builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText("{0} = {1}", entry.Key, entry.Value); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
return; | |
case "set": | |
if (cd.DataArray.Length < 3) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
var setParam = String.Join(" ", cd.DataArray.Skip(2).ToArray()); | |
if (!setParam.Contains("=")) { | |
builder.AppendErrorText( | |
_("Invalid key/value format.") | |
); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
return; | |
} | |
var setKey = setParam.Split('=')[0].Trim(); | |
var setValue = String.Join( | |
"=", setParam.Split('=').Skip(1).ToArray() | |
).Trim(); | |
object oldValue = _UserConfig[setKey]; | |
if (oldValue == null && setKey.StartsWith("MessagePatterns/")) { | |
var id = setKey.Split('/')[1]; | |
var parsedId = Int32.Parse(id); | |
var msgPatternSettings = new MessagePatternListController(_UserConfig); | |
var pattern = msgPatternSettings.Get(parsedId); | |
if (pattern == null) { | |
// pattern does not exist, create it with default values | |
pattern = new MessagePatternModel(parsedId); | |
msgPatternSettings.Add(pattern, parsedId); | |
oldValue = _UserConfig[setKey]; | |
} | |
} | |
if (oldValue == null) { | |
builder.AppendErrorText( | |
_("Invalid config key: '{0}'"), | |
setKey | |
); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
return; | |
} | |
try { | |
object newValue = Convert.ChangeType(setValue, oldValue.GetType()); | |
_UserConfig[setKey] = newValue; | |
builder.AppendText("{0} = {1}", setKey, newValue.ToString()); | |
if (setKey.StartsWith("MessagePatterns/")) { | |
MessageBuilderSettings.ApplyConfig(UserConfig); | |
} | |
} catch (InvalidCastException) { | |
builder.AppendErrorText( | |
_("Could not convert config value: '{0}' to type: {1}"), | |
setValue, | |
oldValue.GetType().Name | |
); | |
} catch (FormatException) { | |
builder.AppendErrorText( | |
_("Could not convert config value: '{0}' to type: {1}"), | |
setValue, | |
oldValue.GetType().Name | |
); | |
} | |
break; | |
case "remove": { | |
if (cd.DataArray.Length < 3) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
var removeParam = cd.DataArray[2]; | |
if (!removeParam.StartsWith("MessagePatterns/")) { | |
builder.AppendErrorText( | |
_("Invalid config remove key: '{0}'. Valid remove " + | |
"keys: MessagePatterns/{{ID}}."), | |
removeParam | |
); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
return; | |
} | |
var id = removeParam.Split('/')[1]; | |
var parsedId = Int32.Parse(id); | |
var patternController = new MessagePatternListController(_UserConfig); | |
var pattern = patternController.Get(parsedId); | |
if (pattern == null) { | |
builder.AppendErrorText( | |
_("Message pattern with ID: '{0}' does not exist."), | |
id | |
); | |
} else { | |
patternController.Remove(parsedId); | |
MessageBuilderSettings.ApplyConfig(UserConfig); | |
builder.AppendText( | |
_("Message pattern with ID: '{0}' removed."), | |
id | |
); | |
} | |
break; | |
} | |
default: | |
builder.AppendErrorText( | |
_("Invalid parameter for config; use load, save, get or set.") | |
); | |
break; | |
} | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
public void CommandShutdown(CommandModel cmd) | |
{ | |
Trace.Call(cmd); | |
#if LOG4NET | |
f_Logger.Info("Shutting down..."); | |
#endif | |
var frontendMgr = cmd != null ? cmd.FrontendManager : null; | |
Shutdown(true, frontendMgr); | |
if (IsLocal) { | |
// allow the frontend to cleanly terminate | |
return; | |
} | |
#if LOG4NET | |
f_Logger.Debug("Terminating process..."); | |
#endif | |
Environment.Exit(0); | |
} | |
public void CommandEcho(CommandModel cmd) | |
{ | |
Trace.Call(cmd); | |
if (cmd == null) { | |
throw new ArgumentNullException("cmd"); | |
} | |
var msg = CreateMessageBuilder(). | |
AppendEventPrefix(). | |
AppendText(cmd.Parameter). | |
ToMessage(); | |
if (cmd.FrontendManager == null) { | |
AddMessageToChat(cmd.Chat, msg, true); | |
} else { | |
cmd.FrontendManager.AddMessageToChat(cmd.Chat, msg); | |
} | |
} | |
public void CommandNetwork(CommandModel cd) | |
{ | |
Trace.Call(cd); | |
if (cd == null) { | |
throw new ArgumentNullException("cd"); | |
} | |
if (cd.DataArray.Length == 1) { | |
// no parameter given, fallback to list | |
_CommandNetworkList(cd); | |
return; | |
} | |
if (cd.DataArray.Length >= 2) { | |
switch (cd.DataArray[1].ToLower()) { | |
case "list": | |
_CommandNetworkList(cd); | |
break; | |
case "switch": | |
_CommandNetworkSwitch(cd); | |
break; | |
case "close": | |
_CommandNetworkClose(cd); | |
break; | |
default: | |
var builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendText( | |
_("Invalid parameter for network; use list, " + | |
"switch, or close") | |
); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
break; | |
} | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
private void _CommandNetworkList(CommandModel cmd) | |
{ | |
var servers = new ServerListController(UserConfig); | |
var availableNetworks = servers.GetNetworks(); | |
var connectedNetworks = new List<IProtocolManager>(); | |
lock (_ProtocolManagers) { | |
foreach (IProtocolManager pm in _ProtocolManagers) { | |
if (pm.IsConnected) { | |
connectedNetworks.Add(pm); | |
if (!String.IsNullOrEmpty(pm.NetworkID)) { | |
availableNetworks.Remove(pm.NetworkID); | |
} | |
} | |
} | |
} | |
var msg = CreateMessageBuilder(); | |
// TRANSLATOR: this line is used as a label / category for a | |
// list of networks below | |
msg.AppendHeader(_("Connected Networks")); | |
AddMessageToFrontend(cmd, msg.ToMessage()); | |
foreach (var network in connectedNetworks) { | |
msg = CreateMessageBuilder(); | |
msg.AppendEventPrefix(); | |
msg.AppendText("{0}: {1} ", _("Network"), network.NetworkID); | |
msg.AppendText("{0}: {1} ", _("Protocol"), network.Protocol); | |
msg.AppendText("{0}: {1} ", _("Host"), network.Host); | |
msg.AppendText("{0}: {1}", _("Port"), network.Port); | |
AddMessageToFrontend(cmd, msg.ToMessage()); | |
} | |
if (connectedNetworks.Count == 0) { | |
msg = CreateMessageBuilder(); | |
// TRANSLATOR: no connected networks | |
msg.AppendEventPrefix().AppendText("<{0}>", _("None")); | |
AddMessageToFrontend(cmd, msg.ToMessage()); | |
} | |
msg = CreateMessageBuilder(); | |
// TRANSLATOR: this line is used as a label / category for a | |
// list of networks below | |
msg.AppendHeader(_("Available Networks")); | |
AddMessageToFrontend(cmd, msg.ToMessage()); | |
foreach (var network in availableNetworks) { | |
if (network == null || network.Trim().Length == 0) { | |
continue; | |
} | |
msg = CreateMessageBuilder(); | |
msg.AppendEventPrefix(); | |
msg.AppendText("{0}: {1}", _("Network"), network); | |
AddMessageToFrontend(cmd, msg.ToMessage()); | |
} | |
if (availableNetworks.Count == 0) { | |
msg = CreateMessageBuilder(); | |
// TRANSLATOR: no available networks | |
msg.AppendEventPrefix().AppendText("<{0}>", _("None")); | |
AddMessageToFrontend(cmd, msg.ToMessage()); | |
} | |
} | |
private void _CommandNetworkClose(CommandModel cd) | |
{ | |
IProtocolManager pm = null; | |
if (cd.DataArray.Length >= 3) { | |
// named protocol manager | |
string network = cd.DataArray[2]; | |
pm = GetProtocolManagerByNetwork(network); | |
if (pm == null) { | |
var builder = CreateMessageBuilder(); | |
builder.AppendText(_("Network close failed - could not " + | |
"find network: {0}"), network); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
return; | |
} | |
} else if (cd.DataArray.Length >= 2) { | |
// network manager of chat | |
pm = cd.Chat.ProtocolManager; | |
} | |
if (pm == null) { | |
return; | |
} | |
// disconnect in background as could be blocking | |
ThreadPool.QueueUserWorkItem(delegate { | |
try { | |
pm.Disconnect(cd.FrontendManager); | |
pm.Dispose(); | |
// Dispose() takes care of removing the chat from session (frontends) | |
lock (_ProtocolManagers) { | |
_ProtocolManagers.Remove(pm); | |
} | |
} catch (Exception ex) { | |
#if LOG4NET | |
f_Logger.Error("_CommandNetworkClose(): Exception", ex); | |
#endif | |
} | |
}); | |
} | |
private void _CommandNetworkSwitch(CommandModel cd) | |
{ | |
FrontendManager fm = cd.FrontendManager; | |
if (fm == null) { | |
_NotEnoughParameters(cd); | |
return; | |
} | |
if (cd.DataArray.Length >= 3) { | |
// named network manager | |
string network = cd.DataArray[2]; | |
var pm = GetProtocolManagerByNetwork(network); | |
if (pm == null) { | |
var builder = CreateMessageBuilder(); | |
builder.AppendText(_("Network switch failed - could not " + | |
"find network: {0}"), network); | |
fm.AddMessageToChat(cd.Chat, builder.ToMessage()); | |
return; | |
} | |
fm.CurrentProtocolManager = pm; | |
fm.UpdateNetworkStatus(); | |
} else if (cd.DataArray.Length >= 2) { | |
// next network manager | |
fm.NextProtocolManager(); | |
} else { | |
_NotEnoughParameters(cd); | |
} | |
} | |
private void _NotConnected(CommandModel cd) | |
{ | |
var builder = CreateMessageBuilder(); | |
builder.AppendText(_("Not connected to any network")); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
private void _NotEnoughParameters(CommandModel cd) | |
{ | |
var builder = CreateMessageBuilder(); | |
builder.AppendText(_("Not enough parameters for {0} command"), | |
cd.Command); | |
AddMessageToFrontend(cd, builder.ToMessage()); | |
} | |
public void UpdateNetworkStatus() | |
{ | |
Trace.Call(); | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.UpdateNetworkStatus(); | |
} | |
} | |
} | |
public T CreateChat<T>(string id, | |
string name, | |
IProtocolManager protocolManager) | |
where T : ChatModel | |
{ | |
Trace.Call(id, name, protocolManager); | |
T chat; | |
Type chatType = typeof(T); | |
if (chatType == typeof(SessionChatModel)) { | |
chat = (T) Activator.CreateInstance(chatType, id, name); | |
} else if (chatType == typeof(PersonChatModel)) { | |
throw new NotSupportedException( | |
"PersonModel is not supported, use " + | |
"Session.CreatePersionChat() instead" | |
); | |
} else { | |
chat = (T) Activator.CreateInstance(chatType, | |
id, name, protocolManager); | |
} | |
chat.ApplyConfig(UserConfig); | |
return chat; | |
} | |
public PersonChatModel CreatePersonChat(PersonModel person, | |
IProtocolManager protocolManager) | |
{ | |
Trace.Call(person, protocolManager); | |
if (person == null) { | |
throw new ArgumentNullException("person"); | |
} | |
return CreatePersonChat(person, person.ID, person.IdentityName, | |
protocolManager); | |
} | |
public PersonChatModel CreatePersonChat(PersonModel person, | |
string id, string name, | |
IProtocolManager protocolManager) | |
{ | |
Trace.Call(person, id, name, protocolManager); | |
var chat = new PersonChatModel(person, id, name, protocolManager); | |
chat.ApplyConfig(UserConfig); | |
return chat; | |
} | |
public void AddChat(ChatModel chat) | |
{ | |
Trace.Call(chat); | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
chat.Position = GetSortedChatPosition(chat); | |
lock (_Chats) { | |
_Chats.Add(chat); | |
if (chat.Position == -1) { | |
chat.Position = _Chats.IndexOf(chat); | |
} else { | |
MoveChat(chat, chat.Position); | |
} | |
} | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.AddChat(chat); | |
} | |
} | |
} | |
public void RemoveChat(ChatModel chat) | |
{ | |
Trace.Call(chat); | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
lock (_Chats) { | |
if (!_Chats.Remove(chat)) { | |
#if LOG4NET | |
f_Logger.Warn("RemoveChat(): _Chats.Remove(" + chat + ") failed, ignoring..."); | |
#endif | |
chat.Close(); | |
return; | |
} | |
chat.Close(); | |
// refresh chat positions | |
foreach (var schat in _Chats) { | |
schat.Position = _Chats.IndexOf(schat); | |
} | |
} | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.RemoveChat(chat); | |
} | |
} | |
} | |
public void EnableChat(ChatModel chat) | |
{ | |
Trace.Call(chat); | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
chat.IsEnabled = true; | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.EnableChat(chat); | |
} | |
} | |
} | |
public void DisableChat(ChatModel chat) | |
{ | |
Trace.Call(chat); | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
chat.IsEnabled = false; | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.DisableChat(chat); | |
} | |
} | |
} | |
public void SyncChat(ChatModel chat) | |
{ | |
Trace.Call(chat); | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.SyncChat(chat); | |
} | |
} | |
} | |
public void MoveChat(ChatModel chat, int newPosition) | |
{ | |
Trace.Call(chat, newPosition); | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
lock (_Chats) { | |
_Chats.Remove(chat); | |
_Chats.Insert(newPosition, chat); | |
foreach (var schat in _Chats) { | |
schat.Position = _Chats.IndexOf(schat); | |
} | |
} | |
} | |
[Obsolete("This method is deprecated, use AddMessageToChat(ChatModel, MessageModel) instead!")] | |
public void AddTextToChat(ChatModel chat, string text) | |
{ | |
AddTextToChat(chat, text, false); | |
} | |
[Obsolete("This method is deprecated, use AddMessageToChat(ChatModel, MessageModel, bool) instead!")] | |
public void AddTextToChat(ChatModel chat, string text, bool ignoreFilters) | |
{ | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
if (text == null) { | |
throw new ArgumentNullException("text"); | |
} | |
AddMessageToChat(chat, new MessageModel(text), ignoreFilters); | |
} | |
public void AddMessageToChat(ChatModel chat, MessageModel msg) | |
{ | |
AddMessageToChat(chat, msg, false); | |
} | |
public void AddMessageToChat(ChatModel chat, MessageModel msg, | |
bool ignoreFilters) | |
{ | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
if (msg == null) { | |
throw new ArgumentNullException("msg"); | |
} | |
bool isFiltered = !ignoreFilters && IsFilteredMessage(chat, msg); | |
LogMessage(chat, msg, isFiltered); | |
if (isFiltered) { | |
return; | |
} | |
lock (chat.MessageBuffer) { | |
try { | |
chat.MessageBuffer.Add(msg); | |
} catch (Exception ex) { | |
#if LOG4NET | |
Trace.Call(chat, msg, ignoreFilters); | |
f_Logger.ErrorFormat( | |
"AddMessageToChat({0}, {1}, {2}): " + | |
"chat.MessageBuffer.Add() threw exception:", | |
chat, msg, ignoreFilters | |
); | |
f_Logger.Error("AddMessageToChat(): ", ex); | |
#endif | |
if (chat.MessageBuffer is Db4oMessageBuffer || | |
chat.MessageBuffer is SqliteMessageBuffer) { | |
#if LOG4NET | |
f_Logger.Error( | |
"AddMessageToChat(): " + | |
"Falling back to volatile message buffer..." | |
); | |
#endif | |
chat.ResetMessageBuffer(); | |
chat.InitMessageBuffer(MessageBufferPersistencyType.Volatile); | |
var builder = CreateMessageBuilder(); | |
builder.AppendEventPrefix(); | |
builder.AppendErrorText( | |
_("Failed to write to chat history. " + | |
"Your chat history will not be preserved. " + | |
"Reason: {0}"), | |
ex.Message | |
); | |
chat.MessageBuffer.Add(builder.ToMessage()); | |
chat.MessageBuffer.Add(msg); | |
} | |
} | |
} | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.AddMessageToChat(chat, msg); | |
} | |
} | |
if (msg.MessageType == MessageType.Event) { | |
// on-event-message | |
OnEventMessage( | |
// at this point we no longer know who sent this nor to whom | |
new EventMessageEventArgs(chat, msg, String.Empty, String.Empty) | |
); | |
} | |
} | |
public void AddPersonToGroupChat(GroupChatModel groupChat, PersonModel person) | |
{ | |
if (groupChat == null) { | |
throw new ArgumentNullException("groupChat"); | |
} | |
if (person == null) { | |
throw new ArgumentNullException("person"); | |
} | |
#if LOG4NET | |
f_Logger.Debug("AddPersonToGroupChat() groupChat.Name: "+groupChat.Name+" person.IdentityName: "+person.IdentityName); | |
#endif | |
groupChat.UnsafePersons.Add(person.ID, person); | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.AddPersonToGroupChat(groupChat, person); | |
} | |
} | |
OnGroupChatPersonAdded( | |
new GroupChatPersonAddedEventArgs(groupChat, person) | |
); | |
} | |
public void UpdatePersonInGroupChat(GroupChatModel groupChat, PersonModel oldPerson, PersonModel newPerson) | |
{ | |
if (groupChat == null) { | |
throw new ArgumentNullException("groupChat"); | |
} | |
if (oldPerson == null) { | |
throw new ArgumentNullException("oldPerson"); | |
} | |
if (newPerson == null) { | |
throw new ArgumentNullException("newPerson"); | |
} | |
#if LOG4NET | |
f_Logger.Debug("UpdatePersonInGroupChat()" + | |
" groupChat.Name: " + groupChat.Name + | |
" oldPerson.IdentityName: " + oldPerson.IdentityName + | |
" newPerson.IdentityName: " + newPerson.IdentityName); | |
#endif | |
// FIXME: do we have to lock groupChat.UnsafePersons here? | |
// probably not, as long as the ProtocolManager who owns this chat | |
// is only running one thread | |
groupChat.UnsafePersons.Remove(oldPerson.ID); | |
groupChat.UnsafePersons.Add(newPerson.ID, newPerson); | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.UpdatePersonInGroupChat(groupChat, oldPerson, newPerson); | |
} | |
} | |
OnGroupChatPersonUpdated( | |
new GroupChatPersonUpdatedEventArgs(groupChat, oldPerson, newPerson) | |
); | |
} | |
public void UpdateTopicInGroupChat(GroupChatModel groupChat, MessageModel topic) | |
{ | |
if (groupChat == null) { | |
throw new ArgumentNullException("groupChat"); | |
} | |
if (topic == null) { | |
throw new ArgumentNullException("topic"); | |
} | |
#if LOG4NET | |
f_Logger.Debug("UpdateTopicInGroupChat() groupChat.Name: " + groupChat.Name + " topic: " + topic.ToString()); | |
#endif | |
groupChat.Topic = topic; | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.UpdateTopicInGroupChat(groupChat, topic); | |
} | |
} | |
} | |
public void RemovePersonFromGroupChat(GroupChatModel groupChat, PersonModel person) | |
{ | |
if (groupChat == null) { | |
throw new ArgumentNullException("groupChat"); | |
} | |
if (person == null) { | |
throw new ArgumentNullException("person"); | |
} | |
#if LOG4NET | |
f_Logger.Debug("RemovePersonFromGroupChat() groupChat.Name: " + groupChat.Name + " person.ID: "+person.ID); | |
#endif | |
groupChat.UnsafePersons.Remove(person.ID); | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.RemovePersonFromGroupChat(groupChat, person); | |
} | |
} | |
OnGroupChatPersonRemoved( | |
new GroupChatPersonRemovedEventArgs(groupChat, person) | |
); | |
} | |
public void SetNetworkStatus(string status) | |
{ | |
if (status == null) { | |
throw new ArgumentNullException("status"); | |
} | |
#if LOG4NET | |
f_Logger.Debug("SetNetworkStatus() status: "+status); | |
#endif | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.SetNetworkStatus(status); | |
} | |
} | |
} | |
public void SetStatus(string status) | |
{ | |
if (status == null) { | |
throw new ArgumentNullException("status"); | |
} | |
#if LOG4NET | |
f_Logger.Debug("SetStatus() status: "+status); | |
#endif | |
lock (_FrontendManagers) { | |
foreach (FrontendManager fm in _FrontendManagers.Values) { | |
fm.SetStatus(status); | |
} | |
} | |
} | |
public IList<string> GetSupportedProtocols() | |
{ | |
return _ProtocolManagerFactory.GetProtocols(); | |
} | |
public IProtocolManager Connect(ServerModel server, FrontendManager frontendManager) | |
{ | |
Trace.Call(server, frontendManager); | |
if (server == null) { | |
throw new ArgumentNullException("server"); | |
} | |
if (String.IsNullOrEmpty(server.Protocol)) { | |
throw new ArgumentNullException("server.Protocol"); | |
} | |
IProtocolManager protocolManager = CreateProtocolManager( | |
server.Protocol | |
); | |
lock (_ProtocolManagers) { | |
_ProtocolManagers.Add(protocolManager); | |
} | |
string password = null; | |
// only pass non-empty passwords to Connect() | |
if (!String.IsNullOrEmpty(server.Password)) { | |
password = server.Password; | |
} | |
if (server.OnConnectCommands != null && server.OnConnectCommands.Count > 0) { | |
protocolManager.Connected += delegate { | |
ThreadPool.QueueUserWorkItem(delegate { | |
try { | |
foreach (string command in server.OnConnectCommands) { | |
if (command.Length == 0) { | |
continue; | |
} | |
try { | |
var cd = new CommandModel( | |
frontendManager, | |
protocolManager.Chat, | |
(string) _UserConfig["Interface/Entry/CommandCharacter"], | |
command | |
); | |
protocolManager.Command(cd); | |
} catch (Exception ex) { | |
#if LOG4NET | |
f_Logger.Error("Connect(): Command in Connected event: Exception", ex); | |
#endif | |
var msg = CreateMessageBuilder(). | |
AppendErrorText("Command '{0}' failed. Reason: {1} ({2})", | |
command, ex.Message, ex.GetType()). | |
ToMessage(); | |
AddMessageToFrontend (frontendManager, protocolManager.Chat, msg); | |
} | |
} | |
} catch (Exception ex) { | |
#if LOG4NET | |
f_Logger.Error("Connect(): Connected event: Exception", ex); | |
#endif | |
} | |
}); | |
}; | |
} | |
protocolManager.Connect(frontendManager, server); | |
if (protocolManager.Chat == null) { | |
// just in case the ProtocolManager is not setting the | |
// protocol chat | |
throw new ApplicationException(_("Connect failed.")); | |
} | |
return protocolManager; | |
} | |
private IProtocolManager CreateProtocolManager(string protocol) | |
{ | |
ProtocolManagerInfoModel info = | |
_ProtocolManagerFactory.GetProtocolManagerInfoByAlias(protocol); | |
if (info == null) { | |
if (_ProtocolManagerFactory.ProtocolManagerInfos.Count != 1) { | |
throw new ArgumentException( | |
String.Format( | |
_("No protocol manager found for the protocol: {0}"), | |
protocol | |
), | |
"protocol" | |
); | |
} | |
// ok, we forgive the user not passing a valid protocol by | |
// falling back to the only available protocol | |
info = _ProtocolManagerFactory.ProtocolManagerInfos[0]; | |
} | |
return _ProtocolManagerFactory.CreateProtocolManager(info, this); | |
} | |
public void LogMessage(ChatModel chat, MessageModel msg, bool isFiltered) | |
{ | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
if (msg == null) { | |
throw new ArgumentNullException("msg"); | |
} | |
if (!(bool) UserConfig["Logging/Enabled"]) { | |
return; | |
} | |
if (isFiltered && !(bool) UserConfig["Logging/LogFilteredMessages"]) { | |
return; | |
} | |
if (chat.ChatType == ChatType.Session || | |
chat.ChatType == ChatType.Protocol) { | |
return; | |
} | |
try { | |
// HACK: twitter retrieves older messages and we don't want to | |
// re-log those when the twitter connection is re-opened | |
var protocol = chat.ProtocolManager.Protocol.ToLower(); | |
if (protocol == "twitter") { | |
return; | |
} | |
// don't log chatstates | |
switch (msg.MessageType) { | |
case MessageType.ChatStateComposing: | |
case MessageType.ChatStatePaused: | |
case MessageType.ChatStateReset: | |
return; | |
} | |
using (var stream = File.AppendText(chat.LogFile)) { | |
stream.WriteLine( | |
String.Format( | |
"[{0:yyyy-MM-dd HH:mm:ss}] {1}", | |
msg.TimeStamp.ToLocalTime(), | |
msg.ToString() | |
) | |
); | |
} | |
} catch (Exception ex) { | |
#if LOG4NET | |
f_Logger.Error("LogMessage(): logging error", ex); | |
#endif | |
} | |
} | |
public bool IsFilteredMessage(ChatModel chat, MessageModel msg) | |
{ | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
if (msg == null) { | |
throw new ArgumentNullException("msg"); | |
} | |
return IsFilteredMessage(chat, msg.ToString(), msg.MessageType); | |
} | |
public bool IsFilteredMessage(ChatModel chat, string msg, | |
MessageType msgType) | |
{ | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
if (msg == null) { | |
throw new ArgumentNullException("msg"); | |
} | |
lock (_Filters) { | |
foreach (var filter in _Filters) { | |
if (!String.IsNullOrEmpty(filter.Protocol) && | |
chat.ProtocolManager != null && | |
filter.Protocol != chat.ProtocolManager.Protocol) { | |
continue; | |
} | |
if (!String.IsNullOrEmpty(filter.NetworkID) && | |
chat.ProtocolManager != null && | |
String.Compare(filter.NetworkID, | |
chat.ProtocolManager.NetworkID, | |
StringComparison.OrdinalIgnoreCase) != 0) { | |
continue; | |
} | |
if (filter.ChatType.HasValue && | |
filter.ChatType != chat.ChatType) { | |
continue; | |
} | |
if (!String.IsNullOrEmpty(filter.ChatID) && | |
!Pattern.IsMatch(chat.ID, filter.ChatID)) { | |
continue; | |
} | |
if (filter.MessageType.HasValue && | |
filter.MessageType != msgType) { | |
continue; | |
} | |
if (!String.IsNullOrEmpty(filter.MessagePattern)) { | |
var pattern = filter.MessagePattern; | |
if (!Pattern.ContainsPatternCharacters(pattern)) { | |
// use globbing by default | |
pattern = String.Format("*{0}*", pattern); | |
} | |
if (!Pattern.IsMatch(msg, pattern)) { | |
continue; | |
} | |
} | |
return true; | |
} | |
} | |
return false; | |
} | |
public void AddMessageToFrontend(CommandModel cmd, MessageModel msg) | |
{ | |
if (cmd == null) { | |
throw new ArgumentNullException("cmd"); | |
} | |
AddMessageToFrontend(cmd.FrontendManager, cmd.Chat, msg); | |
} | |
public void AddMessageToFrontend(FrontendManager fm, ChatModel chat, MessageModel msg) | |
{ | |
if (chat == null) { | |
throw new ArgumentNullException("chat"); | |
} | |
if (msg == null) { | |
throw new ArgumentNullException("msg"); | |
} | |
if (fm == null) { | |
// fallback to session | |
AddMessageToChat(chat, msg); | |
} else { | |
fm.AddMessageToChat(chat, msg); | |
} | |
} | |
void OnUserConfigChanged(object sender, ConfigChangedEventArgs e) | |
{ | |
if (e.Key.StartsWith("Filters/")) { | |
#if LOG4NET | |
f_Logger.Debug("OnUserConfigChanged(): refreshing filters"); | |
#endif | |
// referesh filters | |
// TODO: use a timeout here to only refresh once in 1 second | |
_Filters = _FilterListController.GetFilterList().Values; | |
} | |
if (e.Key == "Interface/Chat/HighlightWords") { | |
MessageBuilderSettings.ApplyConfig(UserConfig); | |
} | |
if (e.Key == "Interface/Notebook/EngineBufferLines") { | |
lock (_Chats) { | |
foreach (var chat in _Chats) { | |
chat.ApplyConfig(UserConfig); | |
} | |
} | |
} | |
} | |
public void CheckPresenceStatus() | |
{ | |
Trace.Call(); | |
var newStatus = PresenceStatus.Unknown; | |
var newMessage = String.Empty; | |
lock (_FrontendManagers) { | |
if (_FrontendManagers.Count == 0) { | |
newStatus = PresenceStatus.Away; | |
newMessage = "away from keyboard"; | |
} else { | |
newStatus = PresenceStatus.Online; | |
} | |
} | |
if (newStatus == PresenceStatus.Unknown) { | |
return; | |
} | |
UpdatePresenceStatus(newStatus, newMessage); | |
} | |
public void Shutdown() | |
{ | |
Shutdown(false, null); | |
} | |
public void Shutdown(bool clean, FrontendManager frontendManager) | |
{ | |
Trace.Call(clean, frontendManager); | |
#if LOG4NET | |
f_Logger.Debug("Shutdown(): flushing all message buffers"); | |
#endif | |
lock (_Chats) { | |
foreach (var chat in _Chats) { | |
try { | |
chat.MessageBuffer.Flush(); | |
} catch (Exception ex) { | |
#if LOG4NET | |
f_Logger.ErrorFormat( | |
"Shutdown(): {0}.MessageBuffer.Flush() " + | |
"failed, continuing with shutdown...", | |
chat.ToString() | |
); | |
f_Logger.Error("Shutdown(): Exception", ex); | |
#endif | |
} | |
} | |
} | |
if (!clean) { | |
return; | |
} | |
#if LOG4NET | |
f_Logger.Debug("Shutdown(): disconnecting and disposing all protocol manangers"); | |
#endif | |
lock (_ProtocolManagers) { | |
foreach (var protocolManager in _ProtocolManagers) { | |
try { | |
protocolManager.Disconnect(frontendManager); | |
protocolManager.Dispose(); | |
} catch (Exception ex) { | |
#if LOG4NET | |
f_Logger.ErrorFormat( | |
"Shutdown(): {0}.Disconnect/Dispose() " + | |
"failed, continuing with shutdown...", | |
protocolManager.ToString() | |
); | |
f_Logger.Error("Shutdown(): Exception", ex); | |
#endif | |
} | |
} | |
} | |
} | |
void UpdatePresenceStatus(PresenceStatus status, string message) | |
{ | |
lock (_ProtocolManagers) { | |
foreach (var manager in _ProtocolManagers) { | |
manager.SetPresenceStatus(status, message); | |
} | |
} | |
} | |
IProtocolManager GetProtocolManagerByHost(string network) | |
{ | |
lock (_ProtocolManagers) { | |
foreach (var manager in _ProtocolManagers) { | |
if (String.Compare(manager.Host, network, true) == 0) { | |
return manager; | |
} | |
} | |
} | |
return null; | |
} | |
IProtocolManager GetProtocolManagerByNetwork(string network) | |
{ | |
lock (_ProtocolManagers) { | |
foreach (var manager in _ProtocolManagers) { | |
if (String.Compare(manager.NetworkID, network, true) == 0) { | |
return manager; | |
} | |
} | |
} | |
return null; | |
} | |
int GetSortedChatPosition(ChatModel chatModel) | |
{ | |
int position = chatModel.Position; | |
if (position != -1) { | |
return position; | |
} | |
ChatType type = chatModel.ChatType; | |
if (type != ChatType.Person && | |
type != ChatType.Group) { | |
return position; | |
} | |
// new group person and group chats behind their protocol chat | |
IProtocolManager pm = chatModel.ProtocolManager; | |
lock (_Chats) { | |
foreach (var chat in _Chats) { | |
if (chat.ChatType == ChatType.Protocol && | |
chat.ProtocolManager == pm) { | |
position = _Chats.IndexOf(chat) + 1; | |
break; | |
} | |
} | |
if (position == -1) { | |
return position; | |
} | |
// now find the first chat with a different protocol manager | |
foreach (var chat in _Chats.Skip(position)) { | |
if (chat.ProtocolManager != pm) { | |
return _Chats.IndexOf(chat); | |
} | |
} | |
} | |
// if there was no next protocol manager, simply append | |
// the chat way to the end | |
return -1; | |
} | |
void InitSessionChat() | |
{ | |
_SessionChat = new SessionChatModel("smuxi", "Smuxi"); | |
_Chats.Add(_SessionChat); | |
var builder = CreateMessageBuilder(); | |
var text = builder.CreateText(_("Welcome to Smuxi")); | |
text.ForegroundColor = new TextColor(255, 0, 0); | |
text.Bold = true; | |
builder.AppendText(text); | |
builder.AppendText(Environment.NewLine); | |
text = builder.CreateText( | |
_("Type /help to get a list of available commands.") | |
); | |
text.Bold = true; | |
builder.AppendText(text); | |
builder.AppendText(Environment.NewLine); | |
text = builder.CreateText(_("After you have made a connection " + | |
"the list of available commands changes. Go to the newly " + | |
"opened connection tab and use the /help command again to " + | |
"see the extended command list.")); | |
text.Bold = true; | |
builder.AppendText(text); | |
builder.AppendText(Environment.NewLine); | |
AddMessageToChat(_SessionChat,builder.ToMessage()); | |
} | |
void UpdateNewsFeed() | |
{ | |
Trace.Call(); | |
try { | |
var proxySettings = new ProxySettings(); | |
proxySettings.ApplyConfig(UserConfig); | |
var url = "http://news.smuxi.org/feed.php"; | |
var req = WebRequest.Create(url); | |
req.Proxy = proxySettings.GetWebProxy(url); | |
if (req is HttpWebRequest) { | |
var httpReq = (HttpWebRequest) req; | |
httpReq.UserAgent = Engine.VersionString; | |
if (NewsFeedLastModified != DateTime.MinValue) { | |
httpReq.IfModifiedSince = NewsFeedLastModified; | |
} | |
} | |
var res = req.GetResponse(); | |
if (res is HttpWebResponse) { | |
var httpRes = (HttpWebResponse) res; | |
if (httpRes.StatusCode == HttpStatusCode.NotModified) { | |
return; | |
} | |
NewsFeedLastModified = httpRes.LastModified; | |
} | |
var feed = AtomFeed.Load(res.GetResponseStream()); | |
var sortedEntries = feed.Entry.OrderBy(x => x.Published); | |
foreach (var entry in sortedEntries) { | |
if (SeenNewsFeedIds.Contains(entry.Id)) { | |
continue; | |
} | |
SeenNewsFeedIds.Add(entry.Id); | |
var msg = new FeedMessageBuilder(); | |
if (IsFirstNewsFeedEntry) { | |
IsFirstNewsFeedEntry = false; | |
msg.AppendText("\n"); | |
msg.AppendHeader("Smuxi News"); | |
msg.AppendText("\n"); | |
} | |
msg.Append(entry); | |
if (!msg.IsEmpty) { | |
msg.AppendText("\n"); | |
AddMessageToChat(SessionChat, msg.ToMessage()); | |
} | |
} | |
} catch (WebException ex) { | |
switch (ex.Status) { | |
case WebExceptionStatus.ConnectFailure: | |
case WebExceptionStatus.ConnectionClosed: | |
case WebExceptionStatus.Timeout: | |
case WebExceptionStatus.ReceiveFailure: | |
case WebExceptionStatus.NameResolutionFailure: | |
case WebExceptionStatus.ProxyNameResolutionFailure: | |
#if LOG4NET | |
f_Logger.Warn( | |
String.Format( | |
"UpdateNewsFeed(): Temporarily issue " + | |
"detected, retrying in {0} min...", | |
NewsFeedRetryInterval.Minutes | |
), | |
ex | |
); | |
#endif | |
NewsFeedTimer.Change(NewsFeedRetryInterval, NewsFeedUpdateInterval); | |
break; | |
} | |
} catch (Exception ex) { | |
#if LOG4NET | |
f_Logger.Error("UpdateNewsFeed(): Exception, ignored...", ex); | |
#endif | |
} | |
} | |
protected virtual void OnGroupChatPersonAdded(GroupChatPersonAddedEventArgs e) | |
{ | |
if (GroupChatPersonAdded != null) { | |
GroupChatPersonAdded(this, e); | |
} | |
var pm = e.GroupChat.ProtocolManager; | |
var hooks = new HookRunner("engine", "session", "on-group-chat-person-added"); | |
hooks.Environments.Add(new ChatHookEnvironment(e.GroupChat)); | |
if (pm != null) { | |
hooks.Environments.Add(new ProtocolManagerHookEnvironment(pm)); | |
} | |
hooks.Environments.Add(new PersonHookEnvironment(e.AddedPerson)); | |
var cmdChar = (string) UserConfig["Interface/Entry/CommandCharacter"]; | |
hooks.Commands.Add(new SessionHookCommand(this, e.GroupChat, cmdChar)); | |
if (pm != null) { | |
hooks.Commands.Add(new ProtocolManagerHookCommand(pm, e.GroupChat, cmdChar)); | |
} | |
// show time | |
hooks.Init(); | |
hooks.Run(); | |
} | |
protected virtual void OnGroupChatPersonRemoved(GroupChatPersonRemovedEventArgs e) | |
{ | |
if (GroupChatPersonRemoved != null) { | |
GroupChatPersonRemoved(this, e); | |
} | |
var pm = e.GroupChat.ProtocolManager; | |
var hooks = new HookRunner("engine", "session", "on-group-chat-person-removed"); | |
hooks.Environments.Add(new ChatHookEnvironment(e.GroupChat)); | |
if (pm != null) { | |
hooks.Environments.Add(new ProtocolManagerHookEnvironment(pm)); | |
} | |
hooks.Environments.Add(new PersonHookEnvironment(e.RemovedPerson)); | |
var cmdChar = (string) UserConfig["Interface/Entry/CommandCharacter"]; | |
hooks.Commands.Add(new SessionHookCommand(this, e.GroupChat, cmdChar)); | |
if (pm != null) { | |
hooks.Commands.Add(new ProtocolManagerHookCommand(pm, e.GroupChat, cmdChar)); | |
} | |
// show time | |
hooks.Init(); | |
hooks.Run(); | |
} | |
protected virtual void OnGroupChatPersonUpdated(GroupChatPersonUpdatedEventArgs e) | |
{ | |
if (GroupChatPersonUpdated != null) { | |
GroupChatPersonUpdated(this, e); | |
} | |
var pm = e.GroupChat.ProtocolManager; | |
var hooks = new HookRunner("engine", "session", "on-group-chat-person-updated"); | |
hooks.Environments.Add(new ChatHookEnvironment(e.GroupChat)); | |
if (pm != null) { | |
hooks.Environments.Add(new ProtocolManagerHookEnvironment(pm)); | |
} | |
hooks.Environments.Add(new PersonHookEnvironment("OLD_", e.OldPerson)); | |
hooks.Environments.Add(new PersonHookEnvironment("NEW_", e.NewPerson)); | |
var cmdChar = (string) UserConfig["Interface/Entry/CommandCharacter"]; | |
hooks.Commands.Add(new SessionHookCommand(this, e.GroupChat, cmdChar)); | |
if (pm != null) { | |
hooks.Commands.Add(new ProtocolManagerHookCommand(pm, e.GroupChat, cmdChar)); | |
} | |
// show time | |
hooks.Init(); | |
hooks.Run(); | |
} | |
protected virtual void OnEventMessage(EventMessageEventArgs e) | |
{ | |
if (EventMessage != null) { | |
EventMessage(this, e); | |
} | |
var pm = e.Chat.ProtocolManager; | |
var hooks = new HookRunner("engine", "session", "on-event-message"); | |
hooks.Environments.Add(new ChatHookEnvironment(e.Chat)); | |
if (pm != null) { | |
hooks.Environments.Add(new ProtocolManagerHookEnvironment(pm)); | |
} | |
hooks.Environments.Add(new MessageHookEnvironment(e.Message, | |
e.Sender, | |
e.Receiver)); | |
var cmdChar = (string) UserConfig["Interface/Entry/CommandCharacter"]; | |
hooks.Commands.Add(new SessionHookCommand(this, e.Chat, cmdChar)); | |
if (pm != null) { | |
hooks.Commands.Add(new ProtocolManagerHookCommand(pm, e.Chat, cmdChar)); | |
} | |
// show time | |
hooks.Init(); | |
hooks.Run(); | |
} | |
private static string _(string msg) | |
{ | |
return LibraryCatalog.GetString(msg, _LibraryTextDomain); | |
} | |
} | |
public abstract class GroupChatEventArgs : EventArgs | |
{ | |
public GroupChatModel GroupChat { get; protected set; } | |
} | |
public class GroupChatPersonAddedEventArgs : GroupChatEventArgs | |
{ | |
public PersonModel AddedPerson { get; private set; } | |
public GroupChatPersonAddedEventArgs(GroupChatModel groupChat, PersonModel addedPerson) | |
{ | |
GroupChat = groupChat; | |
AddedPerson = addedPerson; | |
} | |
} | |
public class GroupChatPersonRemovedEventArgs : GroupChatEventArgs | |
{ | |
public PersonModel RemovedPerson { get; private set; } | |
public GroupChatPersonRemovedEventArgs(GroupChatModel groupChat, PersonModel removedPerson) | |
{ | |
GroupChat = groupChat; | |
RemovedPerson = removedPerson; | |
} | |
} | |
public class GroupChatPersonUpdatedEventArgs : GroupChatEventArgs | |
{ | |
public PersonModel OldPerson { get; private set; } | |
public PersonModel NewPerson { get; private set; } | |
public GroupChatPersonUpdatedEventArgs(GroupChatModel groupChat, | |
PersonModel oldPerson, | |
PersonModel newPerson) | |
{ | |
GroupChat = groupChat; | |
OldPerson = oldPerson; | |
NewPerson = newPerson; | |
} | |
} | |
public class EventMessageEventArgs : EventArgs | |
{ | |
public ChatModel Chat { get; protected set; } | |
public MessageModel Message { get; protected set; } | |
public string Sender { get; protected set; } | |
public string Receiver { get; protected set; } | |
public EventMessageEventArgs(ChatModel chat, MessageModel msg, | |
string sender, string receiver) | |
{ | |
Chat = chat; | |
Message = msg; | |
Sender = sender; | |
Receiver = receiver; | |
} | |
} | |
} |