Skip to content
Snippets Groups Projects
Select Git revision
  • 2e9fe225b78ba8cae06a017bed0f1a11606345dd
  • devel default
  • master
  • fo
  • jirka/typing
  • fo-base
  • mj/submit-images
  • jk/issue-96
  • jk/issue-196
  • honza/add-contestant
  • honza/mr7
  • honza/mrf
  • honza/mrd
  • honza/mra
  • honza/mr6
  • honza/submit-images
  • honza/kolo-vs-soutez
  • jh-stress-test-wip
  • shorten-schools
19 results

users.py

    • Martin Mareš's avatar
      64129d21
      Mail: Přísnější kontroly mailových adres · 64129d21
      Martin Mareš authored
      Funkce email_check_domain je nyní přísnější:
      
        •  Odmítáme adresy, které mají display name. Není jasné, jestli
           nějaké takové byly předtím akceptované, protože jsme odmítali
           adresy s mezerami.
      
        •  S parametrem check_existence=True se ptáme DNS, zda doménová
           část adresy existuje. Vyžadujeme buď MX nebo A záznam. Dotazy
           nicméně mají krátký timeout (aby kontrola moc nebrzdila UI)
           a po něm adresu uznáme jako použitelnou.
      
        •  Domény "nomail" a "test" považujeme za korektní.
      
        •  Zaveden blacklist domén, zatím v něm je jenom obvyklý chyták
           gmail.cz. Možná ho chceme časem přesunout do konfigurace.
      64129d21
      History
      Mail: Přísnější kontroly mailových adres
      Martin Mareš authored
      Funkce email_check_domain je nyní přísnější:
      
        •  Odmítáme adresy, které mají display name. Není jasné, jestli
           nějaké takové byly předtím akceptované, protože jsme odmítali
           adresy s mezerami.
      
        •  S parametrem check_existence=True se ptáme DNS, zda doménová
           část adresy existuje. Vyžadujeme buď MX nebo A záznam. Dotazy
           nicméně mají krátký timeout (aby kontrola moc nebrzdila UI)
           a po něm adresu uznáme jako použitelnou.
      
        •  Domény "nomail" a "test" považujeme za korektní.
      
        •  Zaveden blacklist domén, zatím v něm je jenom obvyklý chyták
           gmail.cz. Možná ho chceme časem přesunout do konfigurace.
    Program.cs 7.96 KiB
    #nullable enable
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    using System.IO;
    
    using Pastel;
    using System.Text.Json;
    using System.Text.Json.Nodes;
    using System.Linq;
    using System.Threading;
    
    using System.CommandLine;
    using System.Runtime.InteropServices;
    
    using Color = System.Drawing.Color;
    using Process = System.Diagnostics.Process;
    
    using Config;
    
    namespace i3csstatus {
    	static class POSIX
    	{
    		[DllImport("libc", SetLastError = true)]
    		public static extern int mkfifo(string path, int mode);
    		[DllImport("libc", CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
    		public static extern int kill(int pid, int sig);
    		public static int SIGUSR1 = 10;
    		public static int EEXIST = 17;
    		public static int ERRNO { get => Marshal.GetLastPInvokeError();}
    	}
    	static class ProcesExtended
    	{
    		public static int KillBySignal(this Process p, int sig)
    		{
    			return POSIX.kill(p.Id, sig);
    		}
    	}
    	static class JsonExtended
    	{
    		public static JsonObject RemoveNull(this JsonObject o)
    		{
    			List<string> toDelete = new();
    			foreach(var (k, v) in o)
    			{
    				if(v == null)
    					toDelete.Add(k);
    			}
    			foreach(var k in toDelete)
    				o.Remove(k);
    			return o;
    		}
    	}
    	static class ColorToHexExtended
    	{
    		static public string ToHex(this Color c)
    		{
    			return $"#{c.R:X2}{c.G:X2}{c.B:X2}";
    		}
    	}
    	record Element(
    			string text,
    			string? short_text=null,
    			Color? color=null
    		);
    
    	#nullable restore
    	abstract class StatusBar
    	{
    		List<Module> modules = new();
    		protected void parseConfigFile(FileInfo configFile)
    		{
    			parseConfigString(File.ReadAllText(configFile.ToString()));
    		}
    		protected void parseConfigString(string configString)
    		{
    			parseConfig(new ConfigParser(configString));
    		}
    		virtual protected void parseConfig(ConfigParser p)
    		{
    			var moduleTypes = new Dictionary<string, Type>(
    					from assembly in AppDomain.CurrentDomain.GetAssemblies()
    					from type in assembly.GetTypes()
    					where type.IsDefined(typeof(ModuleName), false)
    					where typeof(Module).IsAssignableFrom(type)
    					from name in type.GetCustomAttributes(typeof(ModuleName), false)
    					select new KeyValuePair<string, Type>(((ModuleName)name).GetName(), type)
    			);
    
    			var constructorSignature = new Type[]{};
    
    			foreach(var s in p)
    			{
    				string type = s["_type"] ?? s.SectionName;
    				var constructor = moduleTypes[type].GetConstructor(constructorSignature);
    				if(constructor == null)
    					throw new Exception($"Missing constructor of {type} module");
    				Module module = (Module) constructor.Invoke(new object[]{});
    				module.Init(this, s);
    				modules.Add(module);
    			}
    		}
    		public List<Element> Get()
    		{
    			var elements = new List<Element>();
    			foreach(var m in modules)
    				elements.AddRange(m.Get());
    			return elements;
    		}
    		public abstract void Schedule(int in_ms);
    		public abstract T GetGlobal<T>() where T: GlobalModuleResource, new();
    	}
    	class InnerStatusBar: StatusBar
    	{
    		StatusBar parrent;
    		public InnerStatusBar(StatusBar _parrent, ConfigParser p)
    		{
    			parrent = _parrent;
    			parseConfig(p);
    		}
    		public override void Schedule(int in_ms) => parrent.Schedule(in_ms);
    		public override T GetGlobal<T>() => parrent.GetGlobal<T>();
    	}
    	abstract class RootStatusBar: StatusBar
    	{
    		long nextRun = 0;
    		long refresh_ms;
    		object nextRunMonitor = new();
    		public RootStatusBar(FileInfo configFile)
    		{
    			parseConfigFile(configFile);
    		}
    		override protected void parseConfig(ConfigParser p)
    		{
    			refresh_ms = p.MainSection["refresh"]?.AsMs() ?? 1000;
    
    			base.parseConfig(p);
    
    			foreach(var g in globalModuleResources)
    				g.Value.InitEnd();
    		}
    		void wait()
    		{
    			lock(nextRunMonitor)
    			{
    				while(true)
    				{
    					long actual = Environment.TickCount64;
    					if(nextRun <= actual)
    						break;
    					long wait_ms = Math.Min(10000, nextRun - actual);
    					Monitor.Wait(nextRunMonitor, (int)wait_ms, true);
    				}
    				nextRun = Environment.TickCount64 + refresh_ms;
    			}
    		}
    		public void Run(TextWriter w)
    		{
    			initOutput(w);
    			while(true)
    			{
    				wait();
    				Print(w);
    			}
    		}
    		abstract protected void format(TextWriter w, List<Element> elements);
    		virtual protected void initOutput(TextWriter w){}
    		public void Print(TextWriter w)
    		{
    			foreach(var g in globalModuleResources)
    				g.Value.GetBegin();
    			var elements = Get();
    			foreach(var g in globalModuleResources)
    				g.Value.GetEnd();
    			format(w, elements);
    			w.Flush();
    		}
    		public override void Schedule(int in_ms)
    		{
    			lock(nextRunMonitor)
    			{
    				long actual = Environment.TickCount64;
    				if(nextRun <= actual + in_ms)
    					return;
    				nextRun = actual + in_ms;
    				Monitor.Pulse(nextRunMonitor);
    			}
    		}
    		Dictionary<Type, GlobalModuleResource> globalModuleResources = new();
    		public override T GetGlobal<T>()
    		{
    			if(!globalModuleResources.ContainsKey(typeof(T)))
    				globalModuleResources[typeof(T)] = new T();
    			return (T)globalModuleResources[typeof(T)];
    		}
    	}
    	class StatusBarPlainText: RootStatusBar
    	{
    		public StatusBarPlainText(FileInfo configFile):base(configFile){}
    		override protected void format(TextWriter w, List<Element> elements)
    		{
    			bool first = true;
    			foreach(var e in elements)
    			{
    				if(!first)
    					w.Write("|");
    				first = false;
    				w.Write(e.text);
    			}
    			w.WriteLine("");
    		}
    	}
    	class StatusBarTerminal: RootStatusBar
    	{
    		Thread inputThread;
    		public StatusBarTerminal(FileInfo configFile, bool doInput):base(configFile)
    		{
    			if(doInput)
    			{
    				inputThread = new Thread(this.inputThreadFunc);
    				inputThread.IsBackground = true;
    				inputThread.Start();
    			}
    		}
    		void inputThreadFunc()
    		{
    			while(true)
    			{
    				System.ConsoleKeyInfo k = Console.ReadKey();
    				if(k.KeyChar == '\r')
    					Schedule(0);
    				else if(k.KeyChar == 'q')
    					Environment.Exit(0);
    				else
    					Console.WriteLine("");
    			}
    		}
    		override protected void format(TextWriter w, List<Element> elements)
    		{
    			bool first = true;
    			foreach(var e in elements)
    			{
    				if(!first)
    					w.Write("|".Pastel(Color.FromArgb(165, 229, 250)));
    				first = false;
    				w.Write(e.color!=null?e.text.Pastel(e.color.Value):e.text);
    			}
    			w.WriteLine("");
    		}
    	}
    	class StatusBarI3: RootStatusBar
    	{
    		public StatusBarI3(FileInfo configFile):base(configFile){}
    		override protected void initOutput(TextWriter w)
    		{
    			w.WriteLine("{\"version\":1}");
    			w.WriteLine("[{}");
    		}
    		override protected void format(TextWriter w, List<Element> elements)
    		{
    			var json = new JsonArray((from e in elements select new JsonObject(){
    				["full_text"] = e.text,
    				["short_text"] = e.short_text,
    				["color"] = e.color?.ToHex(),
    			}.RemoveNull()).ToArray());
    			var opt = new JsonSerializerOptions();
    			opt.DefaultIgnoreCondition =  System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull;
    			w.Write(",");
    			w.WriteLine(json.ToJsonString(opt));
    		}
    	}
    	class Program
    	{
    	#pragma warning disable 1998
    		static int Main(string[] args)
    		{
    			var configOption = new Option<FileInfo>
    				("--config", "Path to configuration file.");
    			configOption.AddAlias("-c");
    			var rootCommand = new RootCommand("An alternative implementation for i3 status bar generating.");
    			rootCommand.AddGlobalOption(configOption);
    
    			var c_plainText = new Command("plain-text", "Print status bar as plain text.");
    			rootCommand.Add(c_plainText);
    			c_plainText.SetHandler(async (config) =>
    					{
    						(new StatusBarPlainText(config)).Run(Console.Out);
    					}, configOption);
    
    			var c_terminal = new Command("terminal", "Print status bar with terminal escape secvence.");
    			rootCommand.Add(c_terminal);
    			var c_terminal_input = new Option<bool>
    				("--input", "Read key input.");
    			c_terminal_input.AddAlias("-i");
    			c_terminal.Add(c_terminal_input);
    			c_terminal.SetHandler(async (config, input) =>
    					{
    						(new StatusBarTerminal(config, input)).Run(Console.Out);
    					}, configOption, c_terminal_input);
    
    			var c_i3 = new Command("i3", "Comunicate with i3bar.");
    			rootCommand.Add(c_i3);
    			c_i3.SetHandler(async (config) =>
    					{
    						(new StatusBarI3(config)).Run(Console.Out);
    					}, configOption);
    
    			rootCommand.Invoke(args);
    			return 0;
    		}
    	#pragma warning restore 1998
    	}
    }