1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
|
using System;
using System.Collections.Generic;
using System.Linq;
using Duplicati.Library.Interface;
using Duplicati.Library.Logging;
using System.Net.NetworkInformation;
using Duplicati.Library.Modules.Builtin.ResultSerialization;
namespace Duplicati.Library.Modules.Builtin
{
public class SendJabberMessage : ReportHelper
{
/// <summary>
/// The tag used for log messages
/// </summary>
private static readonly string LOGTAG = Logging.Log.LogTagFromType<SendJabberMessage>();
#region Option names
/// <summary>
/// Option used to specify server username
/// </summary>
private const string OPTION_USERNAME = "send-xmpp-username";
/// <summary>
/// Option used to specify server password
/// </summary>
private const string OPTION_PASSWORD = "send-xmpp-password";
/// <summary>
/// Option used to specify recipient(s)
/// </summary>
private const string OPTION_RECIPIENT = "send-xmpp-to";
/// <summary>
/// Option used to specify report body
/// </summary>
private const string OPTION_MESSAGE = "send-xmpp-message";
/// <summary>
/// Option used to specify report level
/// </summary>
private const string OPTION_SENDLEVEL = "send-xmpp-level";
/// <summary>
/// Option used to specify if reports are sent for other operations than backups
/// </summary>
private const string OPTION_SENDALL = "send-xmpp-any-operation";
/// <summary>
/// Option used to specify what format the result is sent in.
/// </summary>
private const string OPTION_RESULT_FORMAT = "send-xmpp-result-output-format";
/// <summary>
/// Option used to set the log level
/// </summary>
private const string OPTION_LOG_LEVEL = "send-xmpp-log-level";
/// <summary>
/// Option used to set the log level
/// </summary>
private const string OPTION_LOG_FILTER = "send-xmpp-log-filter";
/// <summary>
/// Option used to set the maximum number of log lines
/// </summary>
private const string OPTION_MAX_LOG_LINES = "send-xmpp-max-log-lines";
#endregion
#region Option defaults
/// <summary>
/// The default message body
/// </summary>
protected override string DEFAULT_BODY => string.Format("Duplicati %OPERATIONNAME% report for %backup-name%{0}{0}%RESULT%", Environment.NewLine);
/// <summary>
/// Don't use the subject for XMPP
/// </summary>
protected override string DEFAULT_SUBJECT => string.Empty;
#endregion
#region Private variables
/// <summary>
/// The server username
/// </summary>
private string m_username;
/// <summary>
/// The server password
/// </summary>
private string m_password;
/// <summary>
/// The XMPP recipient
/// </summary>
private string m_to;
#endregion
#region Implementation of IGenericModule
/// <summary>
/// The module key, used to activate or deactivate the module on the commandline
/// </summary>
public override string Key { get { return "sendxmpp"; } }
/// <summary>
/// A localized string describing the module with a friendly name
/// </summary>
public override string DisplayName { get { return Strings.SendJabberMessage.DisplayName;} }
/// <summary>
/// A localized description of the module
/// </summary>
public override string Description { get { return Strings.SendJabberMessage.Description; } }
/// <summary>
/// A boolean value that indicates if the module should always be loaded.
/// If true, the user can choose to not load the module by entering the appropriate commandline option.
/// If false, the user can choose to load the module by entering the appropriate commandline option.
/// </summary>
public override bool LoadAsDefault { get { return true; } }
/// <summary>
/// Gets a list of supported commandline arguments
/// </summary>
public override IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument(OPTION_RECIPIENT, CommandLineArgument.ArgumentType.String, Strings.SendJabberMessage.SendxmpptoShort, Strings.SendJabberMessage.SendxmpptoLong),
new CommandLineArgument(OPTION_MESSAGE, CommandLineArgument.ArgumentType.String, Strings.SendJabberMessage.SendxmppmessageShort, Strings.SendJabberMessage.SendxmppmessageLong, DEFAULT_BODY),
new CommandLineArgument(OPTION_USERNAME, CommandLineArgument.ArgumentType.String, Strings.SendJabberMessage.SendxmppusernameShort, Strings.SendJabberMessage.SendxmppusernameLong),
new CommandLineArgument(OPTION_PASSWORD, CommandLineArgument.ArgumentType.String, Strings.SendJabberMessage.SendxmpppasswordShort, Strings.SendJabberMessage.SendxmpppasswordLong),
new CommandLineArgument(OPTION_SENDLEVEL, CommandLineArgument.ArgumentType.Enumeration, Strings.SendJabberMessage.SendxmpplevelShort, Strings.SendJabberMessage.SendxmpplevelLong(ParsedResultType.Success.ToString(), ParsedResultType.Warning.ToString(), ParsedResultType.Error.ToString(), ParsedResultType.Fatal.ToString(), "All"), DEFAULT_LEVEL, null, Enum.GetNames(typeof(ParsedResultType)).Union(new string[] { "All" } ).ToArray()),
new CommandLineArgument(OPTION_SENDALL, CommandLineArgument.ArgumentType.Boolean, Strings.SendJabberMessage.SendxmppanyoperationShort, Strings.SendJabberMessage.SendxmppanyoperationLong),
new CommandLineArgument(OPTION_LOG_LEVEL, CommandLineArgument.ArgumentType.Enumeration, Strings.ReportHelper.OptionLoglevellShort, Strings.ReportHelper.OptionLoglevelLong, DEFAULT_LOG_LEVEL.ToString(), null, Enum.GetNames(typeof(Logging.LogMessageType))),
new CommandLineArgument(OPTION_LOG_FILTER, CommandLineArgument.ArgumentType.String, Strings.ReportHelper.OptionLogfilterShort, Strings.ReportHelper.OptionLogfilterLong),
new CommandLineArgument(OPTION_MAX_LOG_LINES, CommandLineArgument.ArgumentType.Integer, Strings.ReportHelper.OptionmaxloglinesShort, Strings.ReportHelper.OptionmaxloglinesLong, DEFAULT_LOGLINES.ToString()),
new CommandLineArgument(OPTION_RESULT_FORMAT, CommandLineArgument.ArgumentType.Enumeration, Strings.ReportHelper.ResultFormatShort, Strings.ReportHelper.ResultFormatLong(Enum.GetNames(typeof(ResultExportFormat))), DEFAULT_EXPORT_FORMAT.ToString(), null, Enum.GetNames(typeof(ResultExportFormat))),
});
}
}
protected override string SubjectOptionName => OPTION_MESSAGE;
protected override string BodyOptionName => OPTION_MESSAGE;
protected override string ActionLevelOptionName => OPTION_SENDLEVEL;
protected override string ActionOnAnyOperationOptionName => OPTION_SENDALL;
protected override string LogLevelOptionName => OPTION_LOG_LEVEL;
protected override string LogFilterOptionName => OPTION_LOG_FILTER;
protected override string LogLinesOptionName => OPTION_MAX_LOG_LINES;
protected override string ResultFormatOptionName => OPTION_RESULT_FORMAT;
/// <summary>
/// This method is the interception where the module can interact with the execution environment and modify the settings.
/// </summary>
/// <param name="commandlineOptions">A set of commandline options passed to Duplicati</param>
protected override bool ConfigureModule(IDictionary<string, string> commandlineOptions)
{
//We need at least a recipient
commandlineOptions.TryGetValue(OPTION_RECIPIENT, out m_to);
if (string.IsNullOrEmpty(m_to))
return false;
commandlineOptions.TryGetValue(OPTION_USERNAME, out m_username);
commandlineOptions.TryGetValue(OPTION_PASSWORD, out m_password);
return true;
}
#endregion
protected override string ReplaceTemplate(string input, object result, bool subjectline)
{
// No need to do the expansion as we throw away the result
if (subjectline)
return string.Empty;
return base.ReplaceTemplate(input, result, subjectline);
}
protected override void SendMessage(string subject, string body)
{
Exception ex = null;
var waitEvent = new System.Threading.ManualResetEvent(false);
var uri = new Library.Utility.Uri(m_username.Contains("://") ? m_username : "http://" + m_username);
var con = new agsXMPP.XmppClientConnection(uri.Host, uri.Port == -1 ? (uri.Scheme == "https" ? 5223 :5222) : uri.Port);
if (uri.Scheme == "https")
con.UseSSL = true;
var resource = uri.Path ?? "";
if (resource.StartsWith("/", StringComparison.Ordinal))
resource = resource.Substring(1);
if (string.IsNullOrWhiteSpace(resource))
resource = "Duplicati";
agsXMPP.ObjectHandler loginDelegate = (sender) =>
{
try
{
foreach(var recipient in m_to.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries))
con.Send(new agsXMPP.protocol.client.Message(recipient, agsXMPP.protocol.client.MessageType.chat, body));
}
catch (Exception e)
{
Logging.Log.WriteWarningMessage(LOGTAG, "XMPPSendError", e, "Failed to send to XMPP messages: {0}", e.Message);
ex = e;
}
finally
{
waitEvent.Set();
}
};
agsXMPP.ErrorHandler errorHandler = (sender, e) => {
Logging.Log.WriteWarningMessage(LOGTAG, "XMPPError", e, "An error ocurred in XMPP: {0}", e.Message);
ex = e;
waitEvent.Set();
};
agsXMPP.XmppElementHandler loginErroHandler = (sender, e) => {
Logging.Log.WriteWarningMessage(LOGTAG, "XMPPLoginError", null, "Failed to login to XMPP: {0}", e);
ex = new Exception(string.Format("Failed to log in: {0}", e));
waitEvent.Set();
};
con.OnLogin += loginDelegate;
con.OnError += errorHandler;
con.OnAuthError += loginErroHandler;
//con.OnBinded += (sender) => {Console.WriteLine("Binded: {0}", sender);};
//con.OnIq += (sender, iq) => {Console.WriteLine("Iq: {0}", iq);};
//con.OnReadXml += (sender, xml) => {Console.WriteLine("ReadXml: {0}", xml);};
//con.OnWriteXml += (sender, xml) => {Console.WriteLine("writeXml: {0}", xml);};;
con.Open(uri.Username, string.IsNullOrWhiteSpace(m_password) ? uri.Password : m_password, resource);
var timeout = !waitEvent.WaitOne(TimeSpan.FromSeconds(30), true);
con.OnLogin -= loginDelegate;
con.OnError -= errorHandler;
con.OnAuthError -= loginErroHandler;
try
{
con.Close();
}
catch (Exception lex)
{
Logging.Log.WriteExplicitMessage(LOGTAG, "CloseConnectionError", lex, "Failed to close XMPP connection: {0}", lex.Message);
}
if (ex != null)
throw ex;
if (timeout)
throw new TimeoutException(Strings.SendJabberMessage.LoginTimeoutError);
}
}
}
|