Add Windows Chrome side panel and scoped IDE native bridge
This commit is contained in:
1 parent
75afd01b4b
commit
fb6f57cd36
14 files changed
+1018
-1
No files matched your search
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Pipes;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Win32;
|
||||
|
||||
internal static class Bridge {
|
||||
const string Host = "online.turkservis.aiturk";
|
||||
const string Origin = "chrome-extension://ikcplfhgapaabedillejmggjcabpkolp/";
|
||||
const int MaxFrame = 512 * 1024;
|
||||
static readonly object OutputLock = new object();
|
||||
static readonly JavaScriptSerializer Json = new JavaScriptSerializer { MaxJsonLength = MaxFrame };
|
||||
static readonly Encoding Utf8 = new UTF8Encoding(false, true);
|
||||
|
||||
static string ProductDirectory() {
|
||||
string local = Environment.GetEnvironmentVariable("LOCALAPPDATA");
|
||||
if (String.IsNullOrEmpty(local) || !Path.IsPathRooted(local))
|
||||
local = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
return Path.Combine(local, "TurkServis", "AITURK-IDE");
|
||||
}
|
||||
|
||||
static byte[] ReadExactly(Stream source, int count, bool eofAllowed) {
|
||||
var buffer = new byte[count];
|
||||
int offset = 0;
|
||||
while (offset < count) {
|
||||
int read = source.Read(buffer, offset, count - offset);
|
||||
if (read == 0) {
|
||||
if (offset == 0 && eofAllowed) return null;
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
offset += read;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void WriteFrame(Stream output, string message) {
|
||||
byte[] data = Utf8.GetBytes(message);
|
||||
if (data.Length > MaxFrame) throw new InvalidDataException();
|
||||
lock (OutputLock) {
|
||||
byte[] size = BitConverter.GetBytes(data.Length);
|
||||
output.Write(size, 0, size.Length);
|
||||
output.Write(data, 0, data.Length);
|
||||
output.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
[STAThread]
|
||||
static int Main(string[] args) {
|
||||
if (args.Length == 0 || args[0] == "--install" || args[0] == "--uninstall")
|
||||
return Install(args.Length > 0 && args[0] == "--uninstall");
|
||||
if (args[0] != Origin) return 2;
|
||||
Stream output = Console.OpenStandardOutput();
|
||||
try {
|
||||
string discoveryPath = Path.Combine(ProductDirectory(), "chrome-bridge.json");
|
||||
if (new FileInfo(discoveryPath).Length > 4096) throw new InvalidDataException();
|
||||
var discovery = Json.Deserialize<Dictionary<string, object>>(File.ReadAllText(discoveryPath));
|
||||
string pipeName = Convert.ToString(discovery["pipe"]);
|
||||
string token = Convert.ToString(discovery["token"]);
|
||||
if (!System.Text.RegularExpressions.Regex.IsMatch(pipeName, "^aiturk-chrome-[a-f0-9]{32}$") ||
|
||||
!System.Text.RegularExpressions.Regex.IsMatch(token, "^[a-f0-9]{64}$")) throw new InvalidDataException();
|
||||
using (var pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.Asynchronous)) {
|
||||
pipe.Connect(5000);
|
||||
var writer = new StreamWriter(pipe, Utf8) { AutoFlush = true };
|
||||
writer.WriteLine(Json.Serialize(new { token = token }));
|
||||
var reader = new StreamReader(pipe, Utf8, false, 4096, true);
|
||||
var receiver = new Thread(() => {
|
||||
try {
|
||||
// Bounded character-by-character line framing: ReadLine alone
|
||||
// would allocate without a cap for a malformed local peer.
|
||||
var line = new StringBuilder();
|
||||
int value;
|
||||
while ((value = reader.Read()) != -1) {
|
||||
if (value == '\n') { WriteFrame(output, line.ToString()); line.Clear(); }
|
||||
else { line.Append((char)value); if (line.Length > MaxFrame) throw new InvalidDataException(); }
|
||||
}
|
||||
} catch { }
|
||||
Environment.Exit(0);
|
||||
});
|
||||
receiver.IsBackground = true;
|
||||
receiver.Start();
|
||||
Stream input = Console.OpenStandardInput();
|
||||
while (true) {
|
||||
byte[] header = ReadExactly(input, 4, true);
|
||||
if (header == null) break;
|
||||
uint size = BitConverter.ToUInt32(header, 0);
|
||||
if (size == 0 || size > MaxFrame) throw new InvalidDataException();
|
||||
string frame = Utf8.GetString(ReadExactly(input, (int)size, false));
|
||||
// Normalize JSON so embedded newlines cannot inject pipe frames.
|
||||
var message = Json.Deserialize<Dictionary<string, object>>(frame);
|
||||
writer.WriteLine(Json.Serialize(message));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} catch {
|
||||
try { WriteFrame(output, Json.Serialize(new { @event = "error", message = "AITURK IDE bağlantısı bulunamadı. Chrome destekli IDE sürümünü açın; ardından Yeniden bağlan düğmesine basın." })); } catch { }
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int Install(bool remove) {
|
||||
string destination = Path.Combine(ProductDirectory(), "ChromeNativeHost");
|
||||
string keyPath = @"Software\Google\Chrome\NativeMessagingHosts\" + Host;
|
||||
string message = remove
|
||||
? "Chrome bağlantı kaydı kaldırılacak. IDE konuşmaları ve eklenti dosyaları korunur. Devam edilsin mi?"
|
||||
: "AITURK Chrome bağlantısı bu Windows kullanıcısı için kurulacak. Yalnızca AITURK eklentisi, açık IDE'deki konuşmaları okuyup mesaj gönderebilecek. Yönetici yetkisi gerekmez. Devam edilsin mi?";
|
||||
if (MessageBox.Show(message, "AITURK Chrome Bağlantısı", MessageBoxButtons.YesNo, MessageBoxIcon.Information) != DialogResult.Yes) return 0;
|
||||
try {
|
||||
if (remove) {
|
||||
Registry.CurrentUser.DeleteSubKey(keyPath, false);
|
||||
MessageBox.Show("Chrome bağlantı kaydı kaldırıldı. Dosyalar ve konuşmalar korundu.", "AITURK");
|
||||
return 0;
|
||||
}
|
||||
Directory.CreateDirectory(destination);
|
||||
string target = Path.Combine(destination, "AiturkChromeBridge.exe");
|
||||
string source = System.Reflection.Assembly.GetExecutingAssembly().Location;
|
||||
if (!String.Equals(Path.GetFullPath(source), Path.GetFullPath(target), StringComparison.OrdinalIgnoreCase))
|
||||
File.Copy(source, target, true);
|
||||
string manifest = Path.Combine(destination, Host + ".json");
|
||||
File.WriteAllText(manifest, Json.Serialize(new {
|
||||
name = Host, description = "AITURK IDE Chrome bridge", path = target,
|
||||
type = "stdio", allowed_origins = new[] { Origin }
|
||||
}), new UTF8Encoding(false));
|
||||
using (RegistryKey registry = Registry.CurrentUser.CreateSubKey(keyPath)) registry.SetValue("", manifest);
|
||||
MessageBox.Show("Bağlantı kuruldu.\n\n1. Chrome destekli AITURK IDE'yi açın.\n2. Chrome'da chrome://extensions sayfasını açın.\n3. Geliştirici modunu açıp Paketlenmemiş öğe yükle ile ZIP içindeki extension klasörünü seçin.\n4. AITURK simgesine basın.\n\nEklenti klasörünü silmeyin veya taşımayın.", "AITURK Chrome hazır");
|
||||
return 0;
|
||||
} catch (Exception error) {
|
||||
MessageBox.Show("Kurulum tamamlanamadı. Chrome yan panelini kapatıp tekrar deneyin.\n" + error.Message, "AITURK", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user