diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7d86405
--- /dev/null
+++ b/README.md
@@ -0,0 +1,87 @@
+# Snipe-IT bPAC Daemon
+
+## Usage
+
+Install the [bPAC Client Component](https://support.brother.com/g/s/es/dev/en/bpac/download/index.html#client) on the server that runs this daemon.
+
+(TODO: Write detailed guide)
+
+## Start with Windows
+
+Create a shortcut in `shell:startup` folder.
+
+## Start minimized
+
+Just pass `-m` to the argument list.
+
+# labels.blade.php
+
+Replace `resources/views/hardware/labels.blade.php` with content below:
+
+Specifically, if you're using docker, mount it to `/var/www/html/resources/views/hardware/labels.blade.php`.
+
+```php
+ 'ID: ' . $asset->id,
+ 'name' => empty($asset->name) ? '' : 'N: ' . $asset->name,
+ 'serial' => empty($asset->serial) ? '' : 'S: ' . $asset->serial,
+ 'model' => empty($asset->model->name) ? '' : 'M: ' . $asset->model->name,
+ 'company' => $asset->company === null ? null : 'C: ' . $asset->company->name,
+ 'asset_tag' => $asset->asset_tag,
+ ];
+}
+?>
+
+
+
+
+
+
+ Labels Print
+
+
+
+
+
Labels Data:
+
+
+
+
+ ' . $l['id'] . ', ' . $l['asset_tag'] . ', ' . $l['model'] . '');
+ }
+ ?>
+
+
+
+
+
+
+```
diff --git a/SnipeIT-bPAC.sln b/SnipeIT-bPAC.sln
new file mode 100644
index 0000000..435a2b4
--- /dev/null
+++ b/SnipeIT-bPAC.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32804.467
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SnipeIT-bPAC", "SnipeIT-bPAC\SnipeIT-bPAC.csproj", "{4FC36674-2D6F-4DDD-AB2E-5DA6A1B13B26}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4FC36674-2D6F-4DDD-AB2E-5DA6A1B13B26}.Debug|Any CPU.ActiveCfg = Debug|x64
+ {4FC36674-2D6F-4DDD-AB2E-5DA6A1B13B26}.Debug|x86.ActiveCfg = Debug|x86
+ {4FC36674-2D6F-4DDD-AB2E-5DA6A1B13B26}.Debug|x86.Build.0 = Debug|x86
+ {4FC36674-2D6F-4DDD-AB2E-5DA6A1B13B26}.Release|Any CPU.ActiveCfg = Release|x64
+ {4FC36674-2D6F-4DDD-AB2E-5DA6A1B13B26}.Release|x86.ActiveCfg = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {84AD9696-89C8-4A3B-AFBD-30C6889879DB}
+ EndGlobalSection
+EndGlobal
diff --git a/SnipeIT-bPAC/App.config b/SnipeIT-bPAC/App.config
new file mode 100644
index 0000000..c95444a
--- /dev/null
+++ b/SnipeIT-bPAC/App.config
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 8000
+
+
+
+
\ No newline at end of file
diff --git a/SnipeIT-bPAC/HttpListenerContextExtension.cs b/SnipeIT-bPAC/HttpListenerContextExtension.cs
new file mode 100644
index 0000000..f77714c
--- /dev/null
+++ b/SnipeIT-bPAC/HttpListenerContextExtension.cs
@@ -0,0 +1,25 @@
+using System.Net;
+using System.Text;
+using System.Text.Json;
+
+namespace SnipeIT_bPAC
+{
+ public static class HttpListenerContextExtension
+ {
+ public static void SendJson(this HttpListenerContext ctx, object? data)
+ {
+ var resp = ctx.Response;
+ resp.StatusCode = 200;
+ resp.ContentType = "application/json";
+
+ resp.AppendHeader("Access-Control-Allow-Origin", "*");
+ resp.AppendHeader("Access-Control-Allow-Headers", "authorization");
+
+ if (data != null)
+ {
+ resp.OutputStream.Write(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(data)));
+ }
+ resp.OutputStream.Close();
+ }
+ }
+}
diff --git a/SnipeIT-bPAC/Interop.bpac.dll b/SnipeIT-bPAC/Interop.bpac.dll
new file mode 100644
index 0000000..bfe58df
Binary files /dev/null and b/SnipeIT-bPAC/Interop.bpac.dll differ
diff --git a/SnipeIT-bPAC/Program.cs b/SnipeIT-bPAC/Program.cs
new file mode 100644
index 0000000..084a3f2
--- /dev/null
+++ b/SnipeIT-bPAC/Program.cs
@@ -0,0 +1,17 @@
+namespace SnipeIT_bPAC
+{
+ internal static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main(string[] args)
+ {
+ // To customize application configuration such as set high DPI settings or default font,
+ // see https://aka.ms/applicationconfiguration.
+ ApplicationConfiguration.Initialize();
+ Application.Run(new ServerForm(args));
+ }
+ }
+}
\ No newline at end of file
diff --git a/SnipeIT-bPAC/Properties/Settings.Designer.cs b/SnipeIT-bPAC/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..afaff68
--- /dev/null
+++ b/SnipeIT-bPAC/Properties/Settings.Designer.cs
@@ -0,0 +1,62 @@
+//------------------------------------------------------------------------------
+//
+// 此代码由工具生成。
+// 运行时版本:4.0.30319.42000
+//
+// 对此文件的更改可能会导致不正确的行为,并且如果
+// 重新生成代码,这些更改将会丢失。
+//
+//------------------------------------------------------------------------------
+
+namespace SnipeIT_bPAC.Properties {
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.3.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default {
+ get {
+ return defaultInstance;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string AccessKey {
+ get {
+ return ((string)(this["AccessKey"]));
+ }
+ set {
+ this["AccessKey"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("")]
+ public string TemplateFile {
+ get {
+ return ((string)(this["TemplateFile"]));
+ }
+ set {
+ this["TemplateFile"] = value;
+ }
+ }
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("8000")]
+ public ushort ServerPort {
+ get {
+ return ((ushort)(this["ServerPort"]));
+ }
+ set {
+ this["ServerPort"] = value;
+ }
+ }
+ }
+}
diff --git a/SnipeIT-bPAC/Properties/Settings.settings b/SnipeIT-bPAC/Properties/Settings.settings
new file mode 100644
index 0000000..46dc61a
--- /dev/null
+++ b/SnipeIT-bPAC/Properties/Settings.settings
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+ 8000
+
+
+
\ No newline at end of file
diff --git a/SnipeIT-bPAC/ServerForm.Designer.cs b/SnipeIT-bPAC/ServerForm.Designer.cs
new file mode 100644
index 0000000..566117c
--- /dev/null
+++ b/SnipeIT-bPAC/ServerForm.Designer.cs
@@ -0,0 +1,228 @@
+namespace SnipeIT_bPAC
+{
+ partial class ServerForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.components = new System.ComponentModel.Container();
+ this.label1 = new System.Windows.Forms.Label();
+ this.label2 = new System.Windows.Forms.Label();
+ this.textBox_accesskey = new System.Windows.Forms.TextBox();
+ this.textBox_template = new System.Windows.Forms.TextBox();
+ this.checkBox1 = new System.Windows.Forms.CheckBox();
+ this.button_selectFile = new System.Windows.Forms.Button();
+ this.notifyIcon1 = new System.Windows.Forms.NotifyIcon(this.components);
+ this.textBox_log = new System.Windows.Forms.TextBox();
+ this.label3 = new System.Windows.Forms.Label();
+ this.button_start = new System.Windows.Forms.Button();
+ this.button_stop = new System.Windows.Forms.Button();
+ this.numericUpDown_port = new System.Windows.Forms.NumericUpDown();
+ this.linkLabel1 = new System.Windows.Forms.LinkLabel();
+ ((System.ComponentModel.ISupportInitialize)(this.numericUpDown_port)).BeginInit();
+ this.SuspendLayout();
+ //
+ // label1
+ //
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(12, 47);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(75, 17);
+ this.label1.TabIndex = 0;
+ this.label1.Text = "Access Key:";
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(12, 78);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(88, 17);
+ this.label2.TabIndex = 1;
+ this.label2.Text = "Template File:";
+ //
+ // textBox_accesskey
+ //
+ this.textBox_accesskey.Location = new System.Drawing.Point(106, 44);
+ this.textBox_accesskey.Name = "textBox_accesskey";
+ this.textBox_accesskey.PasswordChar = '*';
+ this.textBox_accesskey.Size = new System.Drawing.Size(343, 23);
+ this.textBox_accesskey.TabIndex = 2;
+ this.textBox_accesskey.Leave += new System.EventHandler(this.SaveSettings);
+ //
+ // textBox_template
+ //
+ this.textBox_template.Location = new System.Drawing.Point(106, 75);
+ this.textBox_template.Name = "textBox_template";
+ this.textBox_template.Size = new System.Drawing.Size(318, 23);
+ this.textBox_template.TabIndex = 3;
+ this.textBox_template.Leave += new System.EventHandler(this.SaveSettings);
+ //
+ // checkBox1
+ //
+ this.checkBox1.AutoSize = true;
+ this.checkBox1.Location = new System.Drawing.Point(455, 49);
+ this.checkBox1.Name = "checkBox1";
+ this.checkBox1.Size = new System.Drawing.Size(15, 14);
+ this.checkBox1.TabIndex = 4;
+ this.checkBox1.UseVisualStyleBackColor = true;
+ this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged);
+ //
+ // button_selectFile
+ //
+ this.button_selectFile.Location = new System.Drawing.Point(430, 75);
+ this.button_selectFile.Name = "button_selectFile";
+ this.button_selectFile.Size = new System.Drawing.Size(40, 23);
+ this.button_selectFile.TabIndex = 5;
+ this.button_selectFile.Text = "...";
+ this.button_selectFile.UseVisualStyleBackColor = true;
+ this.button_selectFile.Click += new System.EventHandler(this.button_selectFile_Click);
+ //
+ // notifyIcon1
+ //
+ this.notifyIcon1.Text = "SnipeIT bPAC Daemon";
+ this.notifyIcon1.MouseClick += new System.Windows.Forms.MouseEventHandler(this.notifyIcon1_MouseClick);
+ //
+ // textBox_log
+ //
+ this.textBox_log.Location = new System.Drawing.Point(12, 116);
+ this.textBox_log.Multiline = true;
+ this.textBox_log.Name = "textBox_log";
+ this.textBox_log.ReadOnly = true;
+ this.textBox_log.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
+ this.textBox_log.Size = new System.Drawing.Size(458, 183);
+ this.textBox_log.TabIndex = 6;
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(12, 15);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(76, 17);
+ this.label3.TabIndex = 7;
+ this.label3.Text = "Server Port:";
+ //
+ // button_start
+ //
+ this.button_start.Location = new System.Drawing.Point(314, 12);
+ this.button_start.Name = "button_start";
+ this.button_start.Size = new System.Drawing.Size(75, 23);
+ this.button_start.TabIndex = 9;
+ this.button_start.Text = "Start";
+ this.button_start.UseVisualStyleBackColor = true;
+ this.button_start.Click += new System.EventHandler(this.button_start_Click);
+ //
+ // button_stop
+ //
+ this.button_stop.Enabled = false;
+ this.button_stop.Location = new System.Drawing.Point(395, 12);
+ this.button_stop.Name = "button_stop";
+ this.button_stop.Size = new System.Drawing.Size(75, 23);
+ this.button_stop.TabIndex = 10;
+ this.button_stop.Text = "Stop";
+ this.button_stop.UseVisualStyleBackColor = true;
+ this.button_stop.Click += new System.EventHandler(this.button_stop_Click);
+ //
+ // numericUpDown_port
+ //
+ this.numericUpDown_port.Location = new System.Drawing.Point(106, 12);
+ this.numericUpDown_port.Maximum = new decimal(new int[] {
+ 65535,
+ 0,
+ 0,
+ 0});
+ this.numericUpDown_port.Minimum = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.numericUpDown_port.Name = "numericUpDown_port";
+ this.numericUpDown_port.Size = new System.Drawing.Size(202, 23);
+ this.numericUpDown_port.TabIndex = 11;
+ this.numericUpDown_port.Value = new decimal(new int[] {
+ 1,
+ 0,
+ 0,
+ 0});
+ this.numericUpDown_port.ValueChanged += new System.EventHandler(this.SaveSettings);
+ //
+ // linkLabel1
+ //
+ this.linkLabel1.AutoSize = true;
+ this.linkLabel1.Location = new System.Drawing.Point(235, 302);
+ this.linkLabel1.Name = "linkLabel1";
+ this.linkLabel1.Size = new System.Drawing.Size(235, 17);
+ this.linkLabel1.TabIndex = 12;
+ this.linkLabel1.TabStop = true;
+ this.linkLabel1.Text = "https://github.com/xWTF/SnipeIT-bPAC";
+ this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked);
+ //
+ // ServerForm
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 17F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(482, 328);
+ this.Controls.Add(this.linkLabel1);
+ this.Controls.Add(this.numericUpDown_port);
+ this.Controls.Add(this.button_stop);
+ this.Controls.Add(this.button_start);
+ this.Controls.Add(this.label3);
+ this.Controls.Add(this.textBox_log);
+ this.Controls.Add(this.button_selectFile);
+ this.Controls.Add(this.checkBox1);
+ this.Controls.Add(this.textBox_template);
+ this.Controls.Add(this.textBox_accesskey);
+ this.Controls.Add(this.label2);
+ this.Controls.Add(this.label1);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.MaximizeBox = false;
+ this.Name = "ServerForm";
+ this.Text = "Snipe-IT bPAC Daemon";
+ this.Load += new System.EventHandler(this.ServerForm_Load);
+ this.SizeChanged += new System.EventHandler(this.ServerForm_SizeChanged);
+ this.VisibleChanged += new System.EventHandler(this.ServerForm_VisibleChanged);
+ ((System.ComponentModel.ISupportInitialize)(this.numericUpDown_port)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private Label label1;
+ private Label label2;
+ private TextBox textBox_accesskey;
+ private TextBox textBox_template;
+ private CheckBox checkBox1;
+ private Button button_selectFile;
+ private NotifyIcon notifyIcon1;
+ private TextBox textBox_log;
+ private Label label3;
+ private Button button_start;
+ private Button button_stop;
+ private NumericUpDown numericUpDown_port;
+ private LinkLabel linkLabel1;
+ }
+}
\ No newline at end of file
diff --git a/SnipeIT-bPAC/ServerForm.cs b/SnipeIT-bPAC/ServerForm.cs
new file mode 100644
index 0000000..6258d64
--- /dev/null
+++ b/SnipeIT-bPAC/ServerForm.cs
@@ -0,0 +1,227 @@
+using bpac;
+using System.Diagnostics;
+using System.Net;
+using System.Text;
+using System.Text.Json;
+
+namespace SnipeIT_bPAC
+{
+ public partial class ServerForm : Form
+ {
+ public HttpListener? Listener;
+ public DocumentClass Document = new DocumentClass();
+
+ private bool StartMinimized = false;
+
+ public ServerForm(string[] args)
+ {
+ if (args.Contains("-m"))
+ {
+ StartMinimized = true;
+ }
+
+ InitializeComponent();
+
+ notifyIcon1.Icon = Icon;
+ textBox_log.BackColor = Color.White;
+
+ numericUpDown_port.DataBindings.Add("Value", Properties.Settings.Default, "ServerPort");
+ textBox_accesskey.DataBindings.Add("Text", Properties.Settings.Default, "AccessKey");
+ textBox_template.DataBindings.Add("Text", Properties.Settings.Default, "TemplateFile");
+ }
+
+ protected override void SetVisibleCore(bool value)
+ {
+ if (StartMinimized)
+ {
+ StartMinimized = false;
+ base.SetVisibleCore(false);
+ return;
+ }
+ base.SetVisibleCore(value);
+ }
+
+ private void Log(string data) => Invoke(() =>
+ {
+ textBox_log.AppendText("[" + DateTime.Now.ToString("G") + "] " + data + Environment.NewLine);
+ });
+
+ private void ServerForm_Load(object sender, EventArgs e)
+ {
+ button_start.PerformClick();
+ }
+
+ private void ServerForm_VisibleChanged(object sender, EventArgs e) => notifyIcon1.Visible = !Visible;
+
+ private void ServerForm_SizeChanged(object sender, EventArgs e)
+ {
+ if (WindowState == FormWindowState.Minimized)
+ {
+ Hide();
+ }
+ }
+
+ private void notifyIcon1_MouseClick(object sender, MouseEventArgs e)
+ {
+ Show();
+ WindowState = FormWindowState.Normal;
+ BringToFront();
+ }
+
+ private void checkBox1_CheckedChanged(object sender, EventArgs e)
+ {
+ textBox_accesskey.PasswordChar = checkBox1.Checked ? '\0' : '*';
+ }
+
+ private void button_selectFile_Click(object sender, EventArgs e)
+ {
+ var dialog = new OpenFileDialog()
+ {
+ Title = "Select Template File",
+ Filter = "Template Files|*.lbx|All Files|*.*",
+ CheckFileExists = true,
+ };
+ if (dialog.ShowDialog() == DialogResult.OK)
+ {
+ Properties.Settings.Default.TemplateFile = textBox_template.Text = dialog.FileName;
+ SaveSettings();
+ }
+ }
+
+ private void button_start_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ var port = (ushort)numericUpDown_port.Value;
+
+ Listener = new HttpListener();
+ Listener.Prefixes.Add($"http://127.0.0.1:{port}/");
+
+ Listener.Start();
+ BeginGetContext();
+
+ Log("Server started on port: " + port);
+ }
+ catch (Exception ex)
+ {
+ Log("Unable to start server: " + ex);
+ return;
+ }
+ numericUpDown_port.Enabled = button_start.Enabled = false;
+ button_stop.Enabled = true;
+ }
+
+ private void button_stop_Click(object sender, EventArgs e)
+ {
+ Listener?.Stop();
+ Listener = null;
+ Log("Server stopped");
+
+ numericUpDown_port.Enabled = button_start.Enabled = true;
+ button_stop.Enabled = false;
+ }
+
+ private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) => Process.Start(new ProcessStartInfo
+ {
+ FileName = "https://github.com/xWTF/SnipeIT-bPAC",
+ UseShellExecute = true,
+ });
+
+ private void SaveSettings(object? sender = null, EventArgs? e = null) => Properties.Settings.Default.Save();
+
+ private void HandleRequest(HttpListenerContext ctx)
+ {
+ var req = ctx.Request;
+ if (req.Url?.AbsolutePath != "/print")
+ {
+ ctx.SendJson(new { code = 404 });
+ return;
+ }
+
+ if (req.HttpMethod == "OPTIONS")
+ {
+ ctx.SendJson(null);
+ return;
+ }
+ else if (req.HttpMethod != "POST")
+ {
+ ctx.SendJson(new { code = 403, msg = "bad method" });
+ return;
+ }
+
+ var auth = req.Headers["Authorization"]?.Split(" ");
+ if (auth == null || auth.Length != 2 || auth[0].ToLower() != "bearer" || auth[1] != Invoke(() => textBox_accesskey.Text))
+ {
+ Log("Access denied: " + req.RemoteEndPoint.ToString());
+ ctx.SendJson(new { code = 403, msg = "access denied" });
+ return;
+ }
+
+ using var reader = new StreamReader(req.InputStream, Encoding.UTF8);
+ var requests = JsonSerializer.Deserialize[]>(reader.ReadToEnd());
+ if (requests == null)
+ {
+ ctx.SendJson(new { code = 400, msg = "Unable to deserialize the JSON" });
+ return;
+ }
+
+ try
+ {
+ if (!Document.Open(Invoke(() => textBox_template.Text)))
+ {
+ Log("Print error: Unable to open template");
+ ctx.SendJson(new { code = 500, msg = "Unable to open template" });
+ return;
+ }
+
+ Document.StartPrint(requests.Length + " Labels", PrintOptionConstants.bpoDefault);
+ foreach (var r in requests)
+ {
+ foreach (var kv in r)
+ {
+ var obj = Document.GetObject(kv.Key);
+ if (obj != null)
+ {
+ obj.Text = kv.Value.ToString();
+ }
+ }
+ Document.PrintOut(1, PrintOptionConstants.bpoDefault);
+ }
+ Document.EndPrint();
+ Document.Close();
+
+ Log("Printed " + requests.Length + " labels from " + req.RemoteEndPoint.ToString());
+ }
+ catch (Exception ex)
+ {
+ Log("Print error: " + ex);
+ ctx.SendJson(new { code = 500, msg = "Unknown error occured" });
+ return;
+ }
+ ctx.SendJson(new { code = 200 });
+ }
+
+ private void BeginGetContext() => Listener?.BeginGetContext(new AsyncCallback(ListenerCallback), Listener);
+
+ private void ListenerCallback(IAsyncResult result)
+ {
+ try
+ {
+ if (Listener == null)
+ {
+ return;
+ }
+ HandleRequest(Listener.EndGetContext(result));
+ }
+ catch (ObjectDisposedException)
+ {
+ return;
+ }
+ catch (Exception ex)
+ {
+ Log("Unknown error:" + ex);
+ }
+ BeginGetContext();
+ }
+ }
+}
diff --git a/SnipeIT-bPAC/ServerForm.resx b/SnipeIT-bPAC/ServerForm.resx
new file mode 100644
index 0000000..50794d4
--- /dev/null
+++ b/SnipeIT-bPAC/ServerForm.resx
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/SnipeIT-bPAC/SnipeIT-bPAC.csproj b/SnipeIT-bPAC/SnipeIT-bPAC.csproj
new file mode 100644
index 0000000..e16884b
--- /dev/null
+++ b/SnipeIT-bPAC/SnipeIT-bPAC.csproj
@@ -0,0 +1,43 @@
+
+
+
+ WinExe
+ net6.0-windows
+ SnipeIT_bPAC
+ enable
+ true
+ enable
+ x64;x86
+ xWTF
+ Convenient bPAC print daemon for Snipe-IT asset management software
+ Copyright (c) 2023 xWTF
+
+
+
+
+ tlbimp
+ 3
+ 1
+ 90359d74-b7d9-467f-b938-3883f4cab582
+ 0
+ false
+ False
+
+
+
+
+
+ True
+ True
+ Settings.settings
+
+
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
\ No newline at end of file