private void HandleBackupRequest( HttpListenerContext context, AccessControlStatus acs ) { if( !Allow( acs ) ) { context.Response.StatusCode = 401; context.Response.ContentEncoding = Encoding.UTF8; context.Response.ContentType = "text/html"; using ( var writer = new StreamWriter(context.Response.OutputStream, Encoding.UTF8) ) { writer.Write ( "<html><head>\n" + " <title>" + acs.ToString() + "</title>\n" + "</head><body>\n" + " Your ID has " + ((acs==AccessControlStatus.Blacklisted) ? "been blacklisted" : "not yet been whitelisted") + "<br>\n" + "</body></html>\n" ); } return;// Require auth } // TODO: Handle multiple backup.zip requests Stream zip = null; try { zip = File.Open( Paths.BackupZip, FileMode.Create, FileAccess.ReadWrite, FileShare.None ); } catch ( IOException ) { context.Response.ContentEncoding = Encoding.UTF8; context.Response.ContentType = "text/plain"; context.Response.StatusCode = 503; using ( var writer = new StreamWriter(context.Response.OutputStream, Encoding.UTF8) ) { writer.Write("Couldn't open backup zip (already backing up?)\n"); } return; // EARLY BAIL } using ( zip ) { using ( var package = ZipPackage.Open(zip,FileMode.Create) ) { foreach ( var logfile in Directory.GetFiles(Paths.LogsDirectory,"*.log",SearchOption.TopDirectoryOnly) ) { var relfile = Uri.EscapeDataString( Path.GetFileName(logfile) ); var uri = PackUriHelper.CreatePartUri( new Uri(relfile,UriKind.Relative) ); var part = package.CreatePart( uri, System.Net.Mime.MediaTypeNames.Text.Plain, CompressionOption.Maximum ); using ( var fstream = File.Open(logfile,FileMode.Open,FileAccess.Read,FileShare.ReadWrite) ) using ( var partstream = part.GetStream() ) fstream.CopyTo(partstream); package.Flush(); } package.Close(); } zip.Flush(); zip.Position = 0; context.Response.ContentType = "application/zip"; context.Response.ContentLength64 = zip.Length; zip.CopyTo(context.Response.OutputStream); } return; // EARLY BAIL }
private static void HandleAuthRequest( HttpListenerContext context, ref AccessControlStatus acs ) { var m = reAuthQuery.Match( context.Request.Url.Query ?? "" ); if( m.Success && m.Groups["token"].Success ) { var expiration = DateTime.UtcNow.AddYears(10).ToString("ddd, dd-MMM-yyyy H:mm:ss"); // http://stackoverflow.com/questions/4811009/c-sharp-httplistener-cookies-expiring-after-session-even-though-expiration-time var token = HttpUtility.UrlDecode(m.Groups["token"].Value); context.Response.Headers.Add("Set-Cookie", string.Format("{0}={1};Path=/;Expires={2} GMT",Program.AuthCookieName,token,expiration)); acs = AccessControl.GetStatus(token); } }
bool Allow( AccessControlStatus acs ) { switch( acs ) { case AccessControlStatus.Admin: case AccessControlStatus.Whitelisted: return true; case AccessControlStatus.Pending: case AccessControlStatus.Error: return Program.AutoAllow; case AccessControlStatus.Blacklisted: default: return false; } }
private void HandleLogsRequest( HttpListenerContext context, AccessControlStatus acs, AllLogs logs ) { var vars = context.Request.QueryString; DateTime from, to; int linesOfContext; if (!DateTime.TryParse(vars["from"] ??"",out from )) from = DateTime.Now.AddMinutes(-15); if (!DateTime.TryParse(vars["to"] ??"",out to )) to = DateTime.Now.AddMinutes(+15); if (!int .TryParse(vars["context"]??"",out linesOfContext)) linesOfContext = 0; if ( linesOfContext < 0 ) linesOfContext = 0; if ( linesOfContext > 10000 ) linesOfContext = 10000; string network = vars["server" ] ?? "irc.afternet.org"; string channel = vars["channel"] ?? "#gamedev"; string nickquerys = vars["nickquery"] ?? null; string userquerys = vars["userquery"] ?? null; string hostquerys = vars["hostquery"] ?? null; string querys = vars["query"] ?? null; string querytype = vars["querytype"] ?? "plaintext"; string timefmt = vars["timefmt"] ?? "pst"; Func<string,bool> bools = s => new[]{"true","1"}.Contains((vars[s]??"").ToLowerInvariant()); bool casesensitive = bools("casesensitive"); bool cats = bools("cats"); bool tiny = bools("tiny"); var options = RegexOptions.Compiled | (casesensitive?RegexOptions.None:RegexOptions.IgnoreCase) ; Func<string,Regex> query_to_regex = input => { if ( string.IsNullOrEmpty(input) ) return null; switch ( querytype ) { case "regex": return new Regex(input,options); case "wildcard": return new Regex("^"+Regex.Escape(input).Replace(@"\*","(.*)").Replace(@"\?",".")+"$",options); case "plaintext": return new Regex(Regex.Escape(input),options); default: goto case "plaintext"; } }; Regex nickquery = query_to_regex(nickquerys); Regex userquery = query_to_regex(userquerys); Regex hostquery = query_to_regex(hostquerys); Regex query = query_to_regex(querys ); if ( !string.IsNullOrEmpty(nickquerys) && string.IsNullOrEmpty(userquerys) && string.IsNullOrEmpty(hostquerys) ) { Match nuh = Regexps.IrcWhoMask.Match(nickquerys); if ( nuh.Success ) { var oldqt = querytype; querytype = "wildcard"; nickquery = query_to_regex( nuh.Groups["nick"].Value ); userquery = query_to_regex( nuh.Groups["user"].Value ); hostquery = query_to_regex( nuh.Groups["host"].Value ); querytype = oldqt; } } using ( var writer = new StreamWriter(context.Response.OutputStream) ) { writer.WriteLine("<html><head>"); writer.WriteLine("\t<title>{0} -- {1} ({2} - {3})</title>", network, channel, from, to ); writer.WriteLine("\t<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">"); writer.WriteLine("\t<script type=\"text/javascript\" src=\"http://cdn.jquerytools.org/1.2.7/jquery.tools.min.js\"></script>"); writer.WriteLine("\t<link rel=\"icon\" type=\"image/png\" href=\"/favicon.png\" />"); writer.WriteLine("\t<style type=\"text/css\">"); if ( tiny ) { writer.WriteLine("\t @font-face { font-family: \"04b03\"; src: url(\"/04B_03__.TTF\") format(\"truetype\"); }"); writer.WriteLine("\t * { font-family: \"04b03\"; font-size: 8px; }"); } #if DEBUG writer.WriteLine("\t table, tr, td { cell-spacing: 0; padding: 0; margin: 0; border: 1px solid black; border-collapse: collapse; vertical-align: top; }"); #else writer.WriteLine("\t table, tr, td { cell-spacing: 0; padding: 0; margin: 0; border-collapse: collapse; vertical-align: top; }"); #endif writer.WriteLine("\t a { color: blue; }"); writer.WriteLine("\t a.twit { color: orange; }"); writer.WriteLine("\t a.tor { color: red; }"); writer.WriteLine("\t .link { color: red; }"); writer.WriteLine("\t .tooltip { display: none; background: black; color: white; padding: 5px; }"); writer.WriteLine("\t .matched { background-color: #B0FFB0; }"); writer.WriteLine("\t .notice { color: white; font-weight: bold; background: #AE0E20; }"); writer.WriteLine("\t</style>"); writer.WriteLine("</head><body>"); writer.WriteLine(" <div style=\"7pt; float: right; text-align: right\">"); writer.WriteLine(" Contact: MaulingMonkey in #gamedev [<a href=\"irc://irc.afternet.org/gamedev\">irc://</a>][<a href=\"http://www.gamedev.net/community/chat/\">java</a>]<br>"); writer.WriteLine(" Timestamps are in PST/PDT<BR>"); writer.WriteLine(" </div>"); writer.WriteLine(" <form method=\"get\" action=\".\">"); if ( cats ) writer.WriteLine(" <input type=\"hidden\" value=\"true\" name=\"cats\">"); writer.WriteLine(" <table><tr>"); writer.WriteLine(" <td><table>"); writer.WriteLine(" <tr><td><label>Network:</label></td><td><input name=\"server\" value=\"{0}\"></td></tr>", network); //writer.WriteLine(" <tr><td><label>Channel:</label></td><td><input name=\"channel\" value=\"{0}\"></td></tr>", channel ); if (logs==null || !logs.ContainsKey(network) ) { writer.WriteLine(" <tr><td><label>Channel:</label></td><td><input name=\"channel\" value=\"{0}\"></td></tr>", channel ); } else { writer.WriteLine(" <tr><td><label>Channel:</label></td><td><select name=\"channel\">{0}</select></td></tr>", string.Join("",logs[network].Channels.Select(ch=>((ch==channel)?"<option selected=\"true\">":"<option>")+ch+"</option>").ToArray()) ); } writer.WriteLine(" <tr><td></td><td>Search Parameters</td></tr>"); writer.WriteLine(" <tr><td><label>Nickname:</label></td><td><input name=\"nickquery\" value=\"{0}\"></td><td>(or wildcard mask)</td></tr>", nickquerys ?? "" ); writer.WriteLine(" <tr><td><label>Username:</label></td><td><input name=\"userquery\" value=\"{0}\"></td><td></td></tr>", userquerys ?? "" ); writer.WriteLine(" <tr><td><label>Hostname:</label></td><td><input name=\"hostquery\" value=\"{0}\"></td><td></td></tr>", hostquerys ?? "" ); writer.WriteLine(" <tr><td><label>Message:</label></td><td><input name=\"query\" value=\"{0}\"></td><td></td></tr>", querys ?? "" ); writer.WriteLine(" <tr><td></td><td>"); writer.WriteLine(" <input name=\"casesensitive\" value=\"true\" type=\"checkbox\" {0}> <label>Case Sensitive</label>", casesensitive ? "checked" : "" ); writer.WriteLine(" <br><input name=\"querytype\" value=\"plaintext\" type=\"radio\" {0}> <label>Plain Text</label>" , querytype=="plaintext" ? "checked" : "" ); writer.WriteLine(" <br><input name=\"querytype\" value=\"wildcard\" type=\"radio\" {0}> <label>Wildcard Match</label>", querytype=="wildcard" ? "checked" : "" ); writer.WriteLine(" <br><input name=\"querytype\" value=\"regex\" type=\"radio\" {0}> <label>Regex Match</label>" , querytype=="regex" ? "checked" : "" ); writer.WriteLine(" </td><td></td></tr>"); writer.WriteLine(" </table></td><td><table>"); writer.WriteLine(" <tr><td><label>From: </label></td><td><input name=\"from\" value=\"{0}\"> (12h PST)</td></tr>", from.ToString(Program.Culture) ); writer.WriteLine(" <tr><td><label>To: </label></td><td><input name=\"to\" value=\"{0}\"> (12h PST)</td></tr>", to .ToString(Program.Culture) ); writer.WriteLine(" <tr><td><label>Context:</label></td><td><input name=\"context\" value=\"{0}\"> (12h PST)</td></tr>", linesOfContext ); writer.WriteLine(" <tr><td> </td><td>Display Format:</td></tr>" ); writer.WriteLine(" <tr><td colspan=\"2\"><input name=\"timefmt\" type=\"radio\" value=\"pst\" {0}> <label>M/D H:MM [AM,PM] (PST)</label></td></tr>" , (timefmt=="pst" ) ? "checked" : "" ); writer.WriteLine(" <tr><td colspan=\"2\"><input name=\"timefmt\" type=\"radio\" value=\"longpst\" {0}> <label>M/D/YY H:MM:SS [AM,PM] (PST)</label></td></tr>" , (timefmt=="longpst") ? "checked" : "" ); writer.WriteLine(" <tr><td colspan=\"2\"><input name=\"timefmt\" type=\"radio\" value=\"longutc\" {0}> <label>M/D/YY H:MM:SS (24h) (UTC)</label></td></tr>" , (timefmt=="longutc") ? "checked" : "" ); writer.WriteLine(" <tr><td> </td><td><input type=\"submit\" value=\"Search\"></td></tr>"); writer.WriteLine(" </table></td>"); writer.WriteLine(" </tr></table>"); writer.WriteLine(" </form>"); ChannelLogs clog = null; if ( logs==null ) { writer.WriteLine( "<div class=\"notice\">Logs are currently loading. Reload this page in a minute.</div>"); } else lock (logs) if ( !logs.ContainsKey(network) ) { writer.WriteLine( "<div class=\"notice\">Not serving logs for {0}</div>", network ); } else if ( !logs[network].HasChannel(channel) ) { writer.WriteLine( "<div class=\"notice\">Not serving logs for {0}</div>", channel ); } else { clog = logs[network].Channel(channel); if ( clog.RequireAuth && !Allow(acs) ) { switch( acs ) { case AccessControlStatus.Admin: writer.WriteLine( "<div class=\"notice\">Not (yet) authorized to access channel logs for {0}. You're somehow simultaniously an admin yet not allowed in.</div>", channel ); break; case AccessControlStatus.Whitelisted: writer.WriteLine( "<div class=\"notice\">Not (yet) authorized to access channel logs for {0}. You're somehow simultaniously whitelisted yet not allowed in.</div>", channel ); break; case AccessControlStatus.Pending: writer.WriteLine( "<div class=\"notice\">Not (yet) authorized to access channel logs for {0}. Authorization cookie set, whitelisting pending.</div>", channel ); break; case AccessControlStatus.Error: writer.WriteLine( "<div class=\"notice\">Not (yet) authorized to access channel logs for {0}. PM LoggingMonkey !auth to set an authorization cookie.</div>", channel ); break; case AccessControlStatus.Blacklisted: writer.WriteLine( "<div class=\"notice\">Not (yet) authorized to access channel logs for {0}. Authorization cookie set, whitelisting pending...</div>", channel ); break; } clog = null; } } var pst = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"); if( clog!=null ) { if( clog.RequireAuth && acs == AccessControlStatus.Error ) { writer.WriteLine("<hr>"); writer.WriteLine("<div class=\"notice\">NOTICE: LoggingMonkey will soon switch to a whitelist. You don't currently have an authorization cookie set -- please PM LoggingMonkey !auth for a biodegradable and reusable authorization link. #gamedev ban-ees need not apply.</div>"); } if( acs == AccessControlStatus.Admin ) { writer.WriteLine("<hr>"); writer.WriteLine("<div style=\"background: lightgreen;\">"); writer.WriteLine("Hello admin! You now have access to the following channel commands:"); writer.WriteLine(" !whitelist nick!user@host"); writer.WriteLine(" !blacklist nick!user@host"); writer.WriteLine(" !twit[list] nick!user@host"); writer.WriteLine(" !untwit[list] nick!user@host"); writer.WriteLine("</div>"); } var start2 = DateTime.Now; int linesMatched = 0; int linesWritten = 0; int linesSearched= 0; Action<FastLogReader.Line> write_nuh = (line) => { bool isTor = Tor.Lines.Contains(line.Host) || DnsCache.ResolveDontWait(line.Host).Any(ipv4=>Tor.Lines.Contains(ipv4)); bool isTwit = AccessControl.InTwitlist( line.NUH ); bool showCrossbones = isTor; // || isTwit; string class_ = isTor ? "tor" : isTwit ? "twit" : "normal"; string title = line.NUH + (isTor ? " !TOR!" : isTwit ? " !TWIT!" : ""); string nickFormat = showCrossbones ? "☠ {0} ☠" : "{0}"; writer.Write("<a class='{0}' title='{1}'>",class_,HttpUtility.HtmlEncode(title)); writer.Write( nickFormat, HttpUtility.HtmlEncode( line.Nick ) ); writer.Write("</a>"); }; Action<FastLogReader.Line> write = (line) => { ++linesWritten; writer.Write("["); switch( timefmt ) { case "longpst": writer.Write( line.When.ToString("M/d/yy hh:mm:ss tt",Program.Culture) ); break; case "longutc": writer.Write( TimeZoneInfo.ConvertTimeToUtc( line.When, pst ).ToString("M/d/yy HH:mm:ss",Program.Culture) ); break; case "pst": default: writer.Write(line.When.ToString("M/d h:mm tt",Program.Culture)); break; } writer.Write("] "); switch ( line.Type ) { case FastLogReader.LineType.Action: writer.Write("*"); write_nuh(line); writer.Write(" "); writer.Write(Program.HtmlizeUrls(HttpUtility.HtmlEncode(line.Message),cats)); writer.Write("*<br>\n"); break; case FastLogReader.LineType.Message: writer.Write("<"); write_nuh(line); writer.Write("> "); writer.Write(Program.HtmlizeUrls(HttpUtility.HtmlEncode(line.Message),cats)); writer.Write("<br>\n"); break; case FastLogReader.LineType.Join: writer.Write("-->| "); write_nuh(line); writer.Write(" "); writer.Write(HttpUtility.HtmlEncode(line.Message)); writer.Write("<br>\n"); break; case FastLogReader.LineType.Part: case FastLogReader.LineType.Quit: writer.Write("|<-- "); write_nuh(line); writer.Write(" "); writer.Write(HttpUtility.HtmlEncode(line.Message)); writer.Write("<br>\n"); break; case FastLogReader.LineType.Kick: writer.Write("!<-- "); writer.Write(HttpUtility.HtmlEncode(line.Nick)); writer.Write(" "); writer.Write(HttpUtility.HtmlEncode(line.Message)); writer.Write("<br>\n"); break; case FastLogReader.LineType.Meta: writer.Write("+--+ "); write_nuh(line); writer.Write(" "); writer.Write(HttpUtility.HtmlEncode(line.Message)); writer.Write("<br>\n"); break; default: writer.Write("??? "); writer.Write(HttpUtility.HtmlEncode(line.Message)); writer.Write("<br>\n"); break; } }; int moreContext = -1; Queue<FastLogReader.Line> PreContext = new Queue<FastLogReader.Line>(); if ( linesOfContext==0 ) writer.WriteLine(" <hr>"); bool highlight_matches = (nickquery!=null || hostquery!=null || query!=null) && linesOfContext>0; foreach ( var line in FastLogReader.ReadAllLines(network,channel,from,to) ) { bool lineMatch = ( from <= line.When && line.When <= to ) && ( nickquery == null || nickquery.IsMatch(line.Nick ??"") ) && ( userquery == null || userquery.IsMatch(line.User ??"") ) && ( hostquery == null || hostquery.IsMatch(line.Host ??"") ) && ( query == null || query .IsMatch(line.Message??"") ) ; ++linesSearched; if ( lineMatch ) { ++linesMatched; // write out pre-context and write line if ( linesOfContext!=0 && PreContext.Count>=linesOfContext && moreContext==-1 ) { writer.WriteLine("<hr>"); } while ( PreContext.Count>0 ) { write(PreContext.Dequeue()); } if ( highlight_matches ) writer.Write("<div class=\"matched\">"); write(line); if ( highlight_matches ) writer.Write("</div>"); moreContext = linesOfContext; } else if ( moreContext>0 ) { // not a match, but it's post-context write(line); --moreContext; } else { // not a match, not immediate post-context, start feeding back into pre-context if ( linesOfContext!=0 && PreContext.Count>=linesOfContext ) { PreContext.Dequeue(); moreContext = -1; } if ( linesOfContext!=0 ) PreContext.Enqueue(line); } } var stop2 = DateTime.Now; writer.WriteLine(" <hr>"); writer.WriteLine( "Search matched {1} lines, displayed {2}, searched {3}, and took {0} seconds", (stop2-start2).TotalSeconds.ToString("N2"), linesMatched, linesWritten, linesSearched ); } writer.WriteLine(" <script type='text/javascript'> $(document).ready(function() { $('a[title]').tooltip(); });</script>"); writer.WriteLine("</body></html>"); } }