// Copyright (C) 2014, 2015 Kazuhiro Fujieda // // This program is part of KancolleSniffer. // // KancolleSniffer 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 3 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, see . using System; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Web; using System.Windows.Forms; namespace KancolleSniffer { public class LogServer { private readonly TcpListener _listener; private readonly string _indexDir = Path.GetDirectoryName(Application.ExecutablePath); private string _outputDir = Path.GetDirectoryName(Application.ExecutablePath); public int Port { get; private set; } public string OutputDir { set { _outputDir = value; } } public LogServer(int port) { Port = port; _listener = new TcpListener(IPAddress.Loopback, port); } public void Start() { _listener.Start(); new Thread(Listen).Start(); } private void Listen() { try { while (true) { var socket = _listener.AcceptSocket(); new Thread(Process).Start(socket); } } catch (SocketException) { } finally { _listener.Stop(); } } private void Process(Object obj) { var client = (Socket)obj; var data = new byte[4096]; var from = DateTime.MinValue; var to = DateTime.MaxValue; try { if (client.Receive(data) == 0) return; var request = Encoding.UTF8.GetString(data).Split('\r')[0].Split(' '); if (request.Length != 3) { SendError(client, "400 Bad Request"); return; } if (!request[0].StartsWith("GET", StringComparison.OrdinalIgnoreCase)) { SendError(client, "501 Not Implemented"); return; } var tmp = request[1].Split('?'); var path = HttpUtility.UrlDecode(tmp[0]); if (path == null || !path.StartsWith("/")) { SendError(client, "400 Bad Request"); return; } if (tmp.Length == 2) { var query = HttpUtility.ParseQueryString(tmp[1]); if (query["from"] != null) { double tick; double.TryParse(query["from"], out tick); from = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000); } if (query["to"] != null) { double tick; double.TryParse(query["to"], out tick); to = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000); } } path = path == "/" ? "index.html" : path.Substring(1); var full = Path.Combine(_indexDir, path); var csv = Path.Combine(_outputDir, path); if (path.EndsWith(".html", StringComparison.OrdinalIgnoreCase) && File.Exists(full)) { SendFile(client, full, "text/html"); return; } if (path.EndsWith(".csv", StringComparison.OrdinalIgnoreCase) && File.Exists(csv)) { SendFile(client, csv, "text/csv; charset=Shift_JIS"); return; } if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) { SendJsonData(client, csv, from, to); return; } if (path.EndsWith(".js", StringComparison.OrdinalIgnoreCase) && File.Exists(full)) { SendFile(client, full, "application/javascript"); return; } SendError(client, "404 Not Found"); } catch (IOException) { } catch (SocketException) { } finally { client.Close(); } } private void SendError(Socket client, string error) { using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII)) { writer.Write("HTTP/1.1 {0}\r\n", error); writer.Write("Server: KancolleSniffer\r\n"); writer.Write("Date: {0:R}\r\n", DateTime.Now); writer.Write("Connection: close\r\n\r\n"); writer.Write("{0}\r\n", error); writer.Write("

{0}

\r\n\r\n", error); writer.Flush(); client.Send(((MemoryStream)writer.BaseStream).ToArray()); } } private void SendJsonData(Socket client, string path, DateTime from, DateTime to) { var header = new StreamWriter(new MemoryStream(), Encoding.ASCII); header.Write("HTTP/1.1 200 OK\r\n"); header.Write("Server: KancolleSniffer\r\n"); header.Write("Date: {0:R}\r\n", DateTime.Now); header.Write("Content-Type: {0}\r\n", "application/json; charset=Shift_JIS"); header.Write("Connection: close\r\n\r\n"); header.Flush(); client.Send(((MemoryStream)header.BaseStream).ToArray()); var csv = path.Replace(".json", ".csv"); var encoding = Encoding.GetEncoding("Shift_JIS"); client.Send(encoding.GetBytes("{ \"data\": [\n")); if (File.Exists(csv)) { var delimiter = ""; var material = path.EndsWith("資材ログ.json"); // 末尾の空データを削除する必要がある foreach (var line in File.ReadLines(csv, encoding).Skip(1)) { var data = line.Split(','); DateTime date; if (!DateTime.TryParseExact(data[0], Logger.DateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out date) && DateTime.TryParse(data[0], CultureInfo.CurrentCulture, DateTimeStyles.AssumeLocal, out date)) { data[0] = date.ToString(Logger.DateTimeFormat); } if (date < from || to < date) continue; client.Send(encoding.GetBytes(delimiter + "[\"" + string.Join("\",\"", (material ? data.Take(9) : data)) + "\"]")); delimiter = ",\n"; } } client.Send(encoding.GetBytes("]}\n")); } private void SendFile(Socket client, string path, string mime) { using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII)) { writer.Write("HTTP/1.1 200 OK\r\n"); writer.Write("Server: KancolleSniffer\r\n"); writer.Write("Date: {0:R}\r\n", DateTime.Now); writer.Write("Content-Length: {0}\r\n", new FileInfo(path).Length); writer.Write("Content-Type: {0}\r\n", mime); writer.Write("Connection: close\r\n\r\n"); writer.Flush(); client.SendFile(path, ((MemoryStream)writer.BaseStream).ToArray(), null, TransmitFileOptions.UseDefaultWorkerThread); } } public void Stop() { _listener.Server.Close(); } } }