OSDN Git Service

報告書のJSONを取得する日付の範囲を指定可能にする
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / LogServer.cs
1 // Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // This program is part of KancolleSniffer.\r
4 //\r
5 // KancolleSniffer is free software: you can redistribute it and/or modify\r
6 // it under the terms of the GNU General Public License as published by\r
7 // the Free Software Foundation, either version 3 of the License, or\r
8 // (at your option) any later version.\r
9 //\r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 //\r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 using System;\r
19 using System.Globalization;\r
20 using System.IO;\r
21 using System.Linq;\r
22 using System.Net;\r
23 using System.Net.Sockets;\r
24 using System.Text;\r
25 using System.Threading;\r
26 using System.Web;\r
27 using System.Windows.Forms;\r
28 \r
29 namespace KancolleSniffer\r
30 {\r
31     public class LogServer\r
32     {\r
33         private readonly TcpListener _listener;\r
34         private readonly string _indexDir = Path.GetDirectoryName(Application.ExecutablePath);\r
35         private string _outputDir = Path.GetDirectoryName(Application.ExecutablePath);\r
36 \r
37         public int Port { get; private set; }\r
38 \r
39         public string OutputDir\r
40         {\r
41             set { _outputDir = value; }\r
42         }\r
43 \r
44         public LogServer(int port)\r
45         {\r
46             Port = port;\r
47             _listener = new TcpListener(IPAddress.Loopback, port);\r
48         }\r
49 \r
50         public void Start()\r
51         {\r
52             _listener.Start();\r
53             new Thread(Listen).Start();\r
54         }\r
55 \r
56         private void Listen()\r
57         {\r
58             try\r
59             {\r
60                 while (true)\r
61                 {\r
62                     var socket = _listener.AcceptSocket();\r
63                     new Thread(Process).Start(socket);\r
64                 }\r
65             }\r
66             catch (SocketException)\r
67             {\r
68             }\r
69             finally\r
70             {\r
71                 _listener.Stop();\r
72             }\r
73         }\r
74 \r
75         private void Process(Object obj)\r
76         {\r
77             var client = (Socket)obj;\r
78             var data = new byte[4096];\r
79             var from = DateTime.MinValue;\r
80             var to = DateTime.MaxValue;\r
81             try\r
82             {\r
83                 if (client.Receive(data) == 0)\r
84                     return;\r
85                 var request = Encoding.UTF8.GetString(data).Split('\r')[0].Split(' ');\r
86                 if (request.Length != 3)\r
87                 {\r
88                     SendError(client, "400 Bad Request");\r
89                     return;\r
90                 }\r
91                 if (!request[0].StartsWith("GET", StringComparison.OrdinalIgnoreCase))\r
92                 {\r
93                     SendError(client, "501 Not Implemented");\r
94                     return;\r
95                 }\r
96                 var tmp = request[1].Split('?');\r
97                 var path = HttpUtility.UrlDecode(tmp[0]);\r
98                 if (path == null || !path.StartsWith("/"))\r
99                 {\r
100                     SendError(client, "400 Bad Request");\r
101                     return;\r
102                 }\r
103                 if (tmp.Length == 2)\r
104                 {\r
105                     var query = HttpUtility.ParseQueryString(tmp[1]);\r
106                     if (query["from"] != null)\r
107                     {\r
108                         double tick;\r
109                         double.TryParse(query["from"], out tick);\r
110                         from = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000);\r
111                     }\r
112                     if (query["to"] != null)\r
113                     {\r
114                         double tick;\r
115                         double.TryParse(query["to"], out tick);\r
116                         to = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000);\r
117                     }\r
118                 }\r
119 \r
120                 path = path == "/" ? "index.html" : path.Substring(1);\r
121                 var full = Path.Combine(_indexDir, path);\r
122                 var csv = Path.Combine(_outputDir, path);\r
123                 if (path.EndsWith(".html", StringComparison.OrdinalIgnoreCase) && File.Exists(full))\r
124                 {\r
125                     SendFile(client, full, "text/html");\r
126                     return;\r
127                 }\r
128                 if (path.EndsWith(".csv", StringComparison.OrdinalIgnoreCase) && File.Exists(csv))\r
129                 {\r
130                     SendFile(client, csv, "text/csv; charset=Shift_JIS");\r
131                     return;\r
132                 }\r
133                 if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))\r
134                 {\r
135                     SendJsonData(client, csv, from, to);\r
136                     return;\r
137                 }\r
138                 if (path.EndsWith(".js", StringComparison.OrdinalIgnoreCase) && File.Exists(full))\r
139                 {\r
140                     SendFile(client, full, "application/javascript");\r
141                     return;\r
142                 }\r
143                 SendError(client, "404 Not Found");\r
144             }\r
145             catch (IOException)\r
146             {\r
147             }\r
148             catch (SocketException)\r
149             {\r
150             }\r
151             finally\r
152             {\r
153                 client.Close();\r
154             }\r
155         }\r
156 \r
157         private void SendError(Socket client, string error)\r
158         {\r
159             using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
160             {\r
161                 writer.Write("HTTP/1.1 {0}\r\n", error);\r
162                 writer.Write("Server: KancolleSniffer\r\n");\r
163                 writer.Write("Date: {0:R}\r\n", DateTime.Now);\r
164                 writer.Write("Connection: close\r\n\r\n");\r
165                 writer.Write("<html><head><title>{0}</title></head>\r\n", error);\r
166                 writer.Write("<body><h4>{0}</h4></body></html>\r\n\r\n", error);\r
167                 writer.Flush();\r
168                 client.Send(((MemoryStream)writer.BaseStream).ToArray());\r
169             }\r
170         }\r
171 \r
172         private void SendJsonData(Socket client, string path, DateTime from, DateTime to)\r
173         {\r
174             var header = new StreamWriter(new MemoryStream(), Encoding.ASCII);\r
175             header.Write("HTTP/1.1 200 OK\r\n");\r
176             header.Write("Server: KancolleSniffer\r\n");\r
177             header.Write("Date: {0:R}\r\n", DateTime.Now);\r
178             header.Write("Content-Type: {0}\r\n", "application/json; charset=Shift_JIS");\r
179             header.Write("Connection: close\r\n\r\n");\r
180             header.Flush();\r
181             client.Send(((MemoryStream)header.BaseStream).ToArray());\r
182 \r
183             var csv = path.Replace(".json", ".csv");\r
184             var encoding = Encoding.GetEncoding("Shift_JIS");\r
185             client.Send(encoding.GetBytes("{ \"data\": [\n"));\r
186             if (File.Exists(csv))\r
187             {\r
188                 var delimiter = "";\r
189                 var material = path.EndsWith("資材ログ.json"); // 末尾の空データを削除する必要がある\r
190                 foreach (var line in File.ReadLines(csv, encoding).Skip(1))\r
191                 {\r
192                     var data = line.Split(',');\r
193                     DateTime date;\r
194                     DateTime.TryParseExact(data[0], Logger.DateTimeFormat, CultureInfo.InvariantCulture,\r
195                         DateTimeStyles.AssumeLocal, out date);\r
196                     if (date < from || to < date)\r
197                         continue;\r
198                     client.Send(encoding.GetBytes(delimiter + "[\"" +\r
199                                                   string.Join("\",\"", (material ? data.Take(9) : data)) + "\"]"));\r
200                     delimiter = ",\n";\r
201                 }\r
202             }\r
203             client.Send(encoding.GetBytes("]}\n"));\r
204         }\r
205 \r
206         private void SendFile(Socket client, string path, string mime)\r
207         {\r
208             using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII))\r
209             {\r
210                 writer.Write("HTTP/1.1 200 OK\r\n");\r
211                 writer.Write("Server: KancolleSniffer\r\n");\r
212                 writer.Write("Date: {0:R}\r\n", DateTime.Now);\r
213                 writer.Write("Content-Length: {0}\r\n", new FileInfo(path).Length);\r
214                 writer.Write("Content-Type: {0}\r\n", mime);\r
215                 writer.Write("Connection: close\r\n\r\n");\r
216                 writer.Flush();\r
217                 client.SendFile(path, ((MemoryStream)writer.BaseStream).ToArray(), null,\r
218                     TransmitFileOptions.UseDefaultWorkerThread);\r
219             }\r
220         }\r
221 \r
222         public void Stop()\r
223         {\r
224             _listener.Server.Close();\r
225         }\r
226     }\r
227 }