root/TwitterIrcGateway/trunk/TwitterIrcGatewayCore/Filter.cs

Revision 417, 20.0 kB (checked in by tomoyo, 13 days ago)

bodyを含めているとpostでエラーとなる問題を修正した。

  • Property svn:keywords set to Id
Line 
1using System;
2using System.Collections.Generic;
3using System.Reflection;
4using System.Text;
5using System.Xml.Serialization;
6using System.Text.RegularExpressions;
7using System.Diagnostics;
8using Misuzilla.Net.Irc;
9using System.IO;
10using System.Xml;
11
12namespace Misuzilla.Applications.TwitterIrcGateway.Filter
13{
14    [XmlInclude(typeof(Drop))]
15    [XmlInclude(typeof(Redirect))]
16    [XmlInclude(typeof(RewriteContent))]
17    [XmlInclude(typeof(Process))]
18    public class Filters
19    {
20        public Filters()
21        {
22            _items = new List<FilterItem>();
23        }
24       
25        private static Object _syncObject = new object();
26        private static XmlSerializer _serializer = null;
27        static Filters()
28        {
29            lock (_syncObject)
30            {
31                if (_serializer == null)
32                {
33                    _serializer = new XmlSerializer(typeof(Filters));
34                }
35            }
36        }
37        public static XmlSerializer Serializer
38        {
39            get
40            {
41                return _serializer;
42            }
43        }
44
45        public void Add(FilterItem item)
46        {
47            _items.Add(item);
48        }
49
50        private List<FilterItem> _items;
51        public FilterItem[] Items
52        {
53            get { return _items.ToArray(); }
54            set { _items.AddRange(value); }
55        }
56
57        /// <summary>
58        /// メッセージをフィルタします
59        /// </summary>
60        /// <param name="args"></param>
61        /// <returns>メッセージを捨てるかどうか</returns>
62        public Boolean ExecuteFilters(FilterArgs args)
63        {
64            Trace.WriteLine(String.Format("Filter: User: {0} / Message: {1}",args.User.ScreenName, args.Content.Replace('\n', ' ')));
65            Trace.Indent();
66            foreach (FilterItem item in _items)
67            {
68                if (!item.Enabled)
69                    continue;
70               
71                Trace.WriteLine("=> " + item.GetType().Name);
72                Trace.Indent();
73                try
74                {
75                    Boolean executed = item.Execute(args);
76                    if (args.Drop)
77                    {
78                        Trace.WriteLine(String.Format("=> DROP", item.GetType().Name, args.User.ScreenName, args.Content.Replace('\n', ' ')));
79                        return false;
80                    }
81                    else if (executed)
82                    {
83                        Trace.WriteLine(String.Format("=> Result: User: {1} / Message: {2}", item.GetType().Name,
84                                                      args.User.ScreenName, args.Content.Replace('\n', ' ')));
85                    }
86                }
87                finally
88                {
89                    Trace.Unindent();
90                }
91            }
92            Trace.Unindent();
93
94            return true;
95        }
96
97        /// <summary>
98        ///
99        /// </summary>
100        /// <param name="path"></param>
101        /// <returns></returns>
102        public static Filters Load(String path)
103        {
104            if (File.Exists(path))
105            {
106                Trace.WriteLine(String.Format("Load Filters: {0}", path));
107                try
108                {
109                    using (FileStream fs = new FileStream(path, FileMode.Open))
110                    {
111                        try
112                        {
113                            Filters filters = Filters.Serializer.Deserialize(fs) as Filters;
114                            if (filters != null)
115                            {
116                                foreach (FilterItem item in filters.Items)
117                                {
118                                    Trace.WriteLine(String.Format(" - Filter:{0}", item.ToString()));
119                                }
120                                return filters;
121                            }
122                        }
123                        catch (XmlException xe) { Trace.WriteLine(xe.Message); }
124                        catch (InvalidOperationException ioe) { Trace.WriteLine(ioe.Message); }
125                    }
126                }
127                catch (IOException ie)
128                {
129                    Trace.WriteLine(ie.Message);
130                    throw;
131                }
132            }
133            return new Filters();
134        }
135    }
136
137    public abstract class FilterItem
138    {
139        private Boolean _enabled = true;
140        [XmlAttribute]
141        public Boolean Enabled
142        {
143            get { return _enabled; }
144            set { _enabled = value; }
145        }
146
147        public abstract Boolean Execute(FilterArgs args);
148    }
149
150    public class FilterArgs
151    {
152        public String Content;
153        public User User;
154        public String IRCMessageType;
155        public Boolean Drop;
156        public Session Session;
157        public Status Status;
158
159        public FilterArgs(Session session, String content, User user, String ircMessageType, Boolean drop, Status status)
160        {
161            this.Session = session;
162            this.Content = content;
163            this.User = user;
164            this.IRCMessageType = ircMessageType;
165            this.Drop = drop;
166            this.Status = status;
167        }
168    }
169
170    public class Drop : FilterItem
171    {
172        private String _matchPattern = "";
173        public String MatchPattern
174        {
175            get { return _matchPattern; }
176            set { _matchPattern = value; }
177        }
178       
179        private String _userMatchPattern = "";
180        public String UserMatchPattern
181        {
182            get { return _userMatchPattern; }
183            set { _userMatchPattern = value; }
184        }
185
186        public override Boolean Execute(FilterArgs args)
187        {
188            if (!String.IsNullOrEmpty(_matchPattern))
189            {
190                return args.Drop =
191                    Regex.IsMatch(args.Content, _matchPattern, RegexOptions.IgnoreCase) &&
192                    ((String.IsNullOrEmpty(_userMatchPattern)) ? true : Regex.IsMatch(args.User.ScreenName, _userMatchPattern));
193            }
194            return false;
195        }
196        public override string ToString()
197        {
198            return "Drop:"
199                + ((Enabled) ? "" : "[DISABLED]")
200                + ((String.IsNullOrEmpty(_userMatchPattern)) ? "" : String.Format(" UserMatchPattern={0}", _userMatchPattern))
201                + ((String.IsNullOrEmpty(_matchPattern)) ? "" : String.Format(" MatchPattern={0}", _matchPattern))
202            ;
203        }
204    }
205
206    public class RewriteContent : FilterItem
207    {
208        private String _replacePattern = "";
209        public String ReplacePattern
210        {
211            get { return _replacePattern; }
212            set { _replacePattern = value; }
213        }
214
215        private String _matchPattern = "";
216        public String MatchPattern
217        {
218            get { return _matchPattern; }
219            set { _matchPattern = value; }
220        }
221
222        private String _userMatchPattern = "";
223        public String UserMatchPattern
224        {
225            get { return _userMatchPattern; }
226            set { _userMatchPattern = value; }
227        }
228
229        private String _messageType = "PRIVMSG";
230        public String MessageType
231        {
232            get { return _messageType; }
233            set { _messageType = value; }
234        }
235
236        public override Boolean Execute(FilterArgs args)
237        {
238            if (!String.IsNullOrEmpty(_matchPattern))
239            {
240                if (Regex.IsMatch(args.Content, _matchPattern, RegexOptions.IgnoreCase) &&
241                    ((String.IsNullOrEmpty(_userMatchPattern)) ? true : Regex.IsMatch(args.User.ScreenName, _userMatchPattern)))
242                {
243                    if (!String.IsNullOrEmpty(_replacePattern))
244                    {
245                        args.Content = Regex.Replace(args.Content, _matchPattern, _replacePattern, RegexOptions.IgnoreCase);
246                    }
247
248                    args.IRCMessageType = _messageType;
249                    return true;
250                }
251            }
252            return false;
253        }
254
255        public override string ToString()
256        {
257            return "RewriteContent:"
258                + ((Enabled) ? "" : "[DISABLED]")
259                + ((String.IsNullOrEmpty(_userMatchPattern)) ? "" : String.Format(" UserMatchPattern={0}", _userMatchPattern))
260                + ((String.IsNullOrEmpty(_messageType)) ? "" : String.Format(" MessageType={0}", _messageType))
261                + ((String.IsNullOrEmpty(_matchPattern)) ? "" : String.Format(" MatchPattern={0}", _matchPattern))
262                + ((String.IsNullOrEmpty(_replacePattern)) ? "" : String.Format(" ReplacePattern={0}", _replacePattern))
263            ;
264        }
265    }
266   
267    public class Redirect : FilterItem
268    {
269        private String _matchPattern = "";
270        public String MatchPattern
271        {
272            get { return _matchPattern; }
273            set { _matchPattern = value; }
274        }
275
276        private String _userMatchPattern = "";
277        public String UserMatchPattern
278        {
279            get { return _userMatchPattern; }
280            set { _userMatchPattern = value; }
281        }
282
283        private String _channelName = "";
284        public String ChannelName
285        {
286            get { return _channelName; }
287            set { _channelName = value; }
288        }
289
290        private Boolean _duplicate = true;
291        public Boolean Duplicate
292        {
293            get { return _duplicate; }
294            set { _duplicate = value; }
295        }
296
297        public override Boolean Execute(FilterArgs args)
298        {
299            if (String.IsNullOrEmpty(_channelName))
300                return false;
301
302            if (!String.IsNullOrEmpty(_matchPattern))
303            {
304                Boolean rerouteRequired =
305                    Regex.IsMatch(args.Content, _matchPattern, RegexOptions.IgnoreCase) &&
306                    ((String.IsNullOrEmpty(_userMatchPattern)) ? true : Regex.IsMatch(args.User.ScreenName, _userMatchPattern));
307               
308                if (!rerouteRequired)
309                    return false;
310               
311                IRCMessage msg;
312                switch (args.IRCMessageType.ToUpperInvariant())
313                {
314                    case "NOTICE":
315                        msg = new NoticeMessage(_channelName, args.Content);
316                        break;
317                    case "PRIVMSG":
318                    default:
319                        msg = new PrivMsgMessage(_channelName, args.Content);
320                        break;
321                }
322                msg.SenderNick = args.User.ScreenName;
323                msg.SenderHost = "twitter@" + Server.ServerName;
324                args.Session.Send(msg);
325               
326                if (!_duplicate)
327                    args.Drop = true;
328
329                return true;
330            }
331            return false;
332        }
333        public override string ToString()
334        {
335            return "Redirect:"
336                + ((Enabled) ? "" : "[DISABLED]")
337                + ((String.IsNullOrEmpty(_userMatchPattern)) ? "" : String.Format(" UserMatchPattern={0}", _userMatchPattern))
338                + ((String.IsNullOrEmpty(_matchPattern)) ? "" : String.Format(" MatchPattern={0}", _matchPattern))
339                + ((String.IsNullOrEmpty(_channelName)) ? "" : String.Format(" ChannelName={0}", _channelName))
340                + ((_duplicate) ? " Duplicate" : "")
341            ;
342        }
343    }
344
345    public class Process : FilterItem
346    {
347        private String _replacePattern = "";
348        public String ReplacePattern
349        {
350            get { return _replacePattern; }
351            set { _replacePattern = value; }
352        }
353
354        private String _matchPattern = "";
355        public String MatchPattern
356        {
357            get { return _matchPattern; }
358            set { _matchPattern = value; }
359        }
360
361        private String _userMatchPattern = "";
362        public String UserMatchPattern
363        {
364            get { return _userMatchPattern; }
365            set { _userMatchPattern = value; }
366        }
367
368        private String _messageType = "PRIVMSG";
369        public String MessageType
370        {
371            get { return _messageType; }
372            set { _messageType = value; }
373        }
374
375        public String InputEncoding
376        {
377            get; set;
378        }
379
380        public String OutputEncoding
381        {
382            get; set;
383        }
384       
385        public String ProcessPath
386        {
387            get; set;
388        }
389
390        public String Arguments
391        {
392            get; set;
393        }
394
395        public Boolean XmlMode
396        {
397            get; set;
398        }
399
400        public override Boolean Execute(FilterArgs args)
401        {
402            if (!String.IsNullOrEmpty(_matchPattern))
403            {
404                if (Regex.IsMatch(args.Content, _matchPattern, RegexOptions.IgnoreCase) &&
405                    ((String.IsNullOrEmpty(_userMatchPattern))
406                         ? true
407                         : Regex.IsMatch(args.User.ScreenName, _userMatchPattern)))
408                {
409                    Encoding encIn = (InputEncoding == null)
410                                         ? Encoding.Default
411                                         :(String.Compare(InputEncoding, "UTF-8", true) == 0)
412                                                 ? new UTF8Encoding(false)
413                                                 : Encoding.GetEncoding(InputEncoding);
414
415                    Encoding encOut = (OutputEncoding == null)
416                                          ? Encoding.Default
417                                          : (String.Compare(OutputEncoding, "UTF-8", true) == 0)
418                                                ? new UTF8Encoding(false)
419                                                : Encoding.GetEncoding(OutputEncoding);
420
421                    Trace.WriteLine(String.Format("Start: {0} ({1})", ProcessPath, Arguments));
422                   
423                    ProcessStartInfo psInfo = new ProcessStartInfo(ProcessPath, Arguments);
424                    psInfo.RedirectStandardInput = true;
425                    psInfo.RedirectStandardOutput = true;
426                    psInfo.StandardOutputEncoding = encIn; // Process -> TIG のエンコーディング
427                    psInfo.UseShellExecute = false;
428                    psInfo.WindowStyle = ProcessWindowStyle.Hidden;
429                    psInfo.CreateNoWindow = true;
430
431                    Dictionary<String, String> headers =
432                        new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
433                    String content;
434                    using (System.Diagnostics.Process process = System.Diagnostics.Process.Start(psInfo))
435                    using (StreamWriter sw = new StreamWriter(process.StandardInput.BaseStream, encOut))
436                    {
437                        sw.WriteLine("Url: http://twitter.com/{0}/statuses/{1}", args.Status.User.ScreenName,
438                                     args.Status.Id);
439                        WriteFieldsAndProperties(sw, "User", args.User);
440                        WriteFieldsAndProperties(sw, "Status", args.Status);
441                        sw.WriteLine("Filter-Drop: {0}", args.Drop);
442                        sw.WriteLine("Filter-IRCMessageType: {0}", args.IRCMessageType);
443                        sw.WriteLine();
444                        if (XmlMode)
445                            Status.Serializer.Serialize(sw, args.Status);
446                        else
447                            sw.WriteLine(args.Content);
448                        sw.Close();
449
450                        String output = process.StandardOutput.ReadToEnd();
451                        if (String.IsNullOrEmpty(output))
452                            return false;
453
454                        String[] parts = output.Split(new string[] {"\n\n"}, 2, StringSplitOptions.RemoveEmptyEntries);
455                        if (parts.Length == 1)
456                        {
457                            content = parts[0].Trim();
458                        }
459                        else
460                        {
461                            if (parts.Length == 0)
462                            {
463                                Trace.WriteLine("Filter was recieved invalid data");
464                                return false;
465                            }
466                            foreach (var line in parts[0].Split('\n'))
467                            {
468                                String[] headerParts = line.Split(new char[] {':'}, 2);
469                                if (headerParts.Length != 2)
470                                    continue;
471
472                                headers[headerParts[0].Trim()] = headerParts[1].Trim();
473                            }
474                            content = parts[1].Trim();
475                        }
476
477                        if (process.WaitForExit(60*1000))
478                        {
479                            Trace.WriteLine("Process Exited: " + process.ExitCode.ToString());
480                            // 終了コード見る
481                            if (process.ExitCode == 0)
482                            {
483                                // 書き換え
484                                Boolean drop = args.Drop;
485                                if (headers.ContainsKey("Filter-Drop"))
486                                    Boolean.TryParse(headers["Filter-Drop"], out drop);
487                                if (headers.ContainsKey("Filter-IRCMessageType"))
488                                    args.IRCMessageType = headers["Filter-IRCMessageType"];
489
490                                args.Content = content;
491                            }
492                            else
493                            {
494                                // 何もしない
495                                return false;
496                            }
497                        }
498                    }
499
500                    args.IRCMessageType = _messageType;
501                    return true;
502                }
503            }
504            return false;
505        }
506       
507        private void WriteFieldsAndProperties(TextWriter writer, String name, Object o)
508        {
509            Type t = o.GetType();
510            foreach (MemberInfo mi in t.GetMembers(BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.Instance))
511            {
512                if (mi.Name.StartsWith("_"))
513                    continue;
514               
515                Object value = null;
516                if (mi.MemberType == MemberTypes.Property)
517                {
518                    PropertyInfo pi = (PropertyInfo) mi;
519                    value = pi.GetValue(o, null);
520                }
521                else if (mi.MemberType == MemberTypes.Field)
522                {
523                    FieldInfo fi = (FieldInfo) mi;
524                    value = fi.GetValue(o);
525                }
526                if (value != null)
527                    writer.WriteLine("{0}-{1}: {2}", name, mi.Name, EscapeString(value.ToString()));
528            }
529        }
530        private String EscapeString(String s)
531        {
532            return s.Replace("\\", "\\\\").Replace("\r", "\\r").Replace("\n", "\\n");
533        }
534
535        public override string ToString()
536        {
537            return "Process:"
538                + ((Enabled) ? "" : "[DISABLED]")
539                + String.Format(" ProcessPath={0}", ProcessPath)
540                + ((String.IsNullOrEmpty(_userMatchPattern)) ? "" : String.Format(" UserMatchPattern={0}", _userMatchPattern))
541                + ((String.IsNullOrEmpty(_messageType)) ? "" : String.Format(" MessageType={0}", _messageType))
542                + ((String.IsNullOrEmpty(_matchPattern)) ? "" : String.Format(" MatchPattern={0}", _matchPattern))
543                + ((String.IsNullOrEmpty(_replacePattern)) ? "" : String.Format(" ReplacePattern={0}", _replacePattern))
544            ;
545        }
546    }
547}
Note: See TracBrowser for help on using the browser.