1 // Copyright (C) 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>
\r
3 // Licensed under the Apache License, Version 2.0 (the "License");
\r
4 // you may not use this file except in compliance with the License.
\r
5 // You may obtain a copy of the License at
\r
7 // http://www.apache.org/licenses/LICENSE-2.0
\r
9 // Unless required by applicable law or agreed to in writing, software
\r
10 // distributed under the License is distributed on an "AS IS" BASIS,
\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
12 // See the License for the specific language governing permissions and
\r
13 // limitations under the License.
\r
16 using System.Collections.Generic;
\r
17 using System.Globalization;
\r
20 using System.Net.Sockets;
\r
23 namespace KancolleSniffer
\r
25 public class LogServer
\r
27 private static readonly string IndexDir = AppDomain.CurrentDomain.BaseDirectory;
\r
28 private static string _outputDir = AppDomain.CurrentDomain.BaseDirectory;
\r
30 public static string OutputDir
\r
32 set => _outputDir = value;
\r
35 public static MaterialCount[] MaterialHistory { private get; set; }
\r
37 public static void Process(Socket client, string requestLine)
\r
39 var from = DateTime.MinValue;
\r
40 var to = DateTime.MaxValue;
\r
41 var timestamp = false;
\r
43 var request = requestLine.Split(' ');
\r
44 if (request.Length != 3)
\r
46 SendError(client, "400 Bad Request");
\r
49 if (!request[0].StartsWith("GET", StringComparison.OrdinalIgnoreCase))
\r
51 SendError(client, "501 Not Implemented");
\r
54 var tmp = request[1].Split('?');
\r
55 var path = HttpUtility.UrlDecode(tmp[0]);
\r
56 if (path == null || !path.StartsWith("/"))
\r
58 SendError(client, "400 Bad Request");
\r
61 if (tmp.Length == 2)
\r
63 var query = HttpUtility.ParseQueryString(tmp[1]);
\r
64 if (query["from"] != null)
\r
66 double.TryParse(query["from"], out var tick);
\r
67 from = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000);
\r
69 if (query["to"] != null)
\r
71 double.TryParse(query["to"], out var tick);
\r
72 to = new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(tick / 1000);
\r
74 if (query["number"] != null)
\r
75 timestamp = query["number"] == "true";
\r
78 path = path == "/" ? "index.html" : path.Substring(1);
\r
79 var full = Path.Combine(IndexDir, path);
\r
80 var csv = Path.Combine(_outputDir, path);
\r
81 if (path.EndsWith(".html", StringComparison.OrdinalIgnoreCase) && File.Exists(full))
\r
83 SendFile(client, full, "text/html");
\r
86 if (path.EndsWith(".csv", StringComparison.OrdinalIgnoreCase) && File.Exists(csv))
\r
88 SendFile(client, csv, "text/csv; charset=Shift_JIS");
\r
91 if (path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
\r
93 SendJsonData(client, csv, from, to, timestamp);
\r
96 if (path.EndsWith(".js", StringComparison.OrdinalIgnoreCase) && File.Exists(full))
\r
98 SendFile(client, full, "application/javascript");
\r
101 if (path.EndsWith("proxy.pac"))
\r
103 SendProxyPac(client, HttpProxy.LocalPort);
\r
106 SendError(client, "404 Not Found");
\r
109 private static void SendError(Socket client, string error)
\r
111 using (var writer = new StreamWriter(new MemoryStream(), Encoding.ASCII))
\r
113 writer.Write("HTTP/1.1 {0}\r\n", error);
\r
114 writer.Write("Server: KancolleSniffer\r\n");
\r
115 writer.Write("Date: {0:R}\r\n", DateTime.Now);
\r
116 writer.Write("Connection: close\r\n\r\n");
\r
117 writer.Write("<html><head><title>{0}</title></head>\r\n", error);
\r
118 writer.Write("<body><h4>{0}</h4></body></html>\r\n\r\n", error);
\r
120 client.Send(((MemoryStream)writer.BaseStream).ToArray());
\r
124 private static void SendJsonData(Socket client, string path, DateTime from, DateTime to, bool number)
\r
126 using (var header = new StreamWriter(new MemoryStream(), Encoding.ASCII))
\r
128 header.Write("HTTP/1.1 200 OK\r\n");
\r
129 header.Write("Server: KancolleSniffer\r\n");
\r
130 header.Write("Date: {0:R}\r\n", DateTime.Now);
\r
131 header.Write("Content-Type: {0}\r\n", "application/json; charset=Shift_JIS");
\r
132 header.Write("Connection: close\r\n\r\n");
\r
134 client.Send(((MemoryStream)header.BaseStream).ToArray());
\r
136 var csv = path.Replace(".json", ".csv");
\r
137 var encoding = Encoding.GetEncoding("Shift_JIS");
\r
138 client.Send(encoding.GetBytes("{ \"data\": [\n"));
\r
139 var battle = false;
\r
140 var material = false;
\r
143 if (!File.Exists(csv))
\r
146 if (path.EndsWith("遠征報告書.json"))
\r
150 else if (path.EndsWith("改修報告書.json"))
\r
154 else if (path.EndsWith("海戦・ドロップ報告書.json"))
\r
159 else if (path.EndsWith("開発報告書.json"))
\r
163 else if (path.EndsWith("建造報告書.json"))
\r
167 else if (path.EndsWith("資材ログ.json"))
\r
172 else if (path.EndsWith("戦果.json"))
\r
176 var delimiter = "";
\r
177 foreach (var line in File.ReadLines(csv, encoding).Skip(1))
\r
179 var data = line.Split(',');
\r
180 if (!DateTime.TryParseExact(data[0], Logger.DateTimeFormat, CultureInfo.InvariantCulture,
\r
181 DateTimeStyles.AssumeLocal, out DateTime date))
\r
183 if (DateTime.TryParse(data[0], CultureInfo.CurrentCulture,
\r
184 DateTimeStyles.AssumeLocal, out date))
\r
186 data[0] = date.ToString(Logger.DateTimeFormat);
\r
193 if (date < from || to < date)
\r
195 IEnumerable<string> entries = data;
\r
197 entries = data.Take(9);
\r
199 entries = ProcessBattleLog(data);
\r
200 if (entries.Count() != records)
\r
204 var stamp = ((date.ToUniversalTime().Ticks -
\r
205 new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks) /
\r
206 TimeSpan.TicksPerMillisecond).ToString();
\r
207 client.Send(encoding.GetBytes(delimiter + "[" + stamp + "," +
\r
208 string.Join(",", entries.Skip(1)) + "]"));
\r
212 client.Send(encoding.GetBytes(delimiter + "[\"" +
\r
213 string.Join("\",\"", entries) + "\"]"));
\r
217 if (material && !number)
\r
219 client.Send(encoding.GetBytes(delimiter + "[\"" +
\r
220 string.Join("\",\"", GetCurrentMaterialRecord()) + "\"]"));
\r
225 client.Send(encoding.GetBytes("]}\n"));
\r
229 private static IEnumerable<string> GetCurrentMaterialRecord()
\r
231 return new[] {DateTime.Now.ToString(Logger.DateTimeFormat)}.
\r
232 Concat(MaterialHistory.Select(c => c.Now.ToString()));
\r
235 private static IEnumerable<string> ProcessBattleLog(string[] data)
\r
237 if (data.Length == 35)
\r
238 data = data.Concat(Enumerable.Repeat("", 3)).ToArray();
\r
239 if (data.Length != 38)
\r
241 if (data[5] == "T字戦(有利)")
\r
243 if (data[5] == "T字戦(不利)")
\r
245 if (data[6].EndsWith("航行序列"))
\r
246 data[6] = data[6].Substring(0, 4);
\r
247 if (data[7].EndsWith("航行序列"))
\r
248 data[7] = data[7].Substring(0, 4);
\r
249 data[37] = ShortenAirBattleResult(data[37]);
\r
250 return AddDamagedShip(data);
\r
253 private static string ShortenAirBattleResult(string result)
\r
272 private static IEnumerable<string> AddDamagedShip(string[] data)
\r
274 var damaged = new List<string>();
\r
275 for (var i = 11; i < 11 + 12; i += 2)
\r
277 var ship = data[i];
\r
280 var hp = data[i + 1];
\r
283 if (ship.Contains("・"))
\r
285 var ships = ship.Split('・');
\r
286 var hps = hp.Split('・');
\r
287 var nowMax = hps[0].Split('/').Select(int.Parse).ToArray();
\r
288 if (ShipStatus.CalcDamage(nowMax[0], nowMax[1]) == ShipStatus.Damage.Badly)
\r
289 damaged.Add(ships[0]);
\r
290 nowMax = hps[1].Split('/').Select(int.Parse).ToArray();
\r
291 if (ShipStatus.CalcDamage(nowMax[0], nowMax[1]) == ShipStatus.Damage.Badly)
\r
292 damaged.Add(ships[1]);
\r
296 var nowMax = hp.Split('/').Select(int.Parse).ToArray();
\r
297 if (ShipStatus.CalcDamage(nowMax[0], nowMax[1]) == ShipStatus.Damage.Badly)
\r
301 catch (FormatException)
\r
306 return data.Take(23).Concat(new[] {string.Join("・", damaged)}).Concat(data.Skip(23));
\r
309 private static void SendFile(Socket client, string path, string mime)
\r
311 using (var header = new StreamWriter(new MemoryStream(), Encoding.ASCII))
\r
313 header.Write("HTTP/1.1 200 OK\r\n");
\r
314 header.Write("Server: KancolleSniffer\r\n");
\r
315 header.Write("Date: {0:R}\r\n", DateTime.Now);
\r
316 header.Write("Content-Length: {0}\r\n", new FileInfo(path).Length);
\r
317 header.Write("Content-Type: {0}\r\n", mime);
\r
318 header.Write("Connection: close\r\n\r\n");
\r
320 client.SendFile(path, ((MemoryStream)header.BaseStream).ToArray(), null,
\r
321 TransmitFileOptions.UseDefaultWorkerThread);
\r
325 private static void SendProxyPac(Socket client, int port)
\r
327 using (var header = new StreamWriter(new MemoryStream(), Encoding.ASCII))
\r
329 header.Write("HTTP/1.1 200 OK\r\n");
\r
330 header.Write("Server: KancolleSniffer\r\n");
\r
331 header.Write("Date: {0:R}\r\n", DateTime.Now);
\r
332 header.Write("Content-Type: application/x-ns-proxy-autoconfig\r\n");
\r
333 header.Write("Connection: close\r\n\r\n");
\r
335 client.Send(((MemoryStream)header.BaseStream).ToArray());
\r
338 function FindProxyForURL(url, host) {
\r
339 if(isInNet(host, ""203.104.209.71"", ""255.255.255.255"") ||
\r
340 isInNet(host, ""125.6.184.15"", ""255.255.255.255"") ||
\r
341 isInNet(host, ""125.6.184.16"", ""255.255.255.255"") ||
\r
342 isInNet(host, ""125.6.187.205"", ""255.255.255.255"") ||
\r
343 isInNet(host, ""125.6.187.229"", ""255.255.255.255"") ||
\r
344 isInNet(host, ""125.6.187.253"", ""255.255.255.255"") ||
\r
345 isInNet(host, ""125.6.188.25"", ""255.255.255.255"") ||
\r
346 isInNet(host, ""203.104.248.135"", ""255.255.255.255"") ||
\r
347 isInNet(host, ""125.6.189.7"", ""255.255.255.255"") ||
\r
348 isInNet(host, ""125.6.189.39"", ""255.255.255.255"") ||
\r
349 isInNet(host, ""125.6.189.71"", ""255.255.255.255"") ||
\r
350 isInNet(host, ""125.6.189.103"", ""255.255.255.255"") ||
\r
351 isInNet(host, ""125.6.189.135"", ""255.255.255.255"") ||
\r
352 isInNet(host, ""125.6.189.167"", ""255.255.255.255"") ||
\r
353 isInNet(host, ""125.6.189.215"", ""255.255.255.255"") ||
\r
354 isInNet(host, ""125.6.189.247"", ""255.255.255.255"") ||
\r
355 isInNet(host, ""203.104.209.23"", ""255.255.255.255"") ||
\r
356 isInNet(host, ""203.104.209.39"", ""255.255.255.255"") ||
\r
357 isInNet(host, ""203.104.209.55"", ""255.255.255.255"") ||
\r
358 isInNet(host, ""203.104.209.102"", ""255.255.255.255"") ||
\r
359 isInNet(host, ""203.104.209.87"", ""255.255.255.255"")) {
\r
360 return ""PROXY 127.0.0.1:8080"";
\r
365 }".Replace("8080", port.ToString());
\r
366 client.Send(Encoding.ASCII.GetBytes(pacFile));
\r