{"id":466274,"date":"2025-07-07T15:23:12","date_gmt":"2025-07-07T15:23:12","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=466274"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=466274","title":{"rendered":"<span>\u041e\u0442 PHP \u043a Zig: \u043a\u0430\u043a \u044f \u0438\u0437\u0431\u0430\u0432\u0438\u043b\u0441\u044f \u043e\u0442 \u043a\u043e\u0441\u0442\u044b\u043b\u0435\u0439 \u0432 \u0431\u0438\u043b\u043b\u0438\u043d\u0433-\u0441\u0438\u0441\u0442\u0435\u043c\u0435<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u042f full-cycle \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u0440\u0430\u0431\u043e\u0442\u0430\u044e \u0441 Rust\/Zig\/Go \u0438 \u0443\u0432\u043b\u0435\u043a\u0430\u044e\u0441\u044c APL \u043a\u0430\u043a \u043e\u0441\u043e\u0431\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u043c\u044b\u0448\u043b\u0435\u043d\u0438\u044f \u043e \u043a\u043e\u0434\u0435. \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u044f \u043f\u0438\u0448\u0443 \u043d\u0430 \u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u044f\u0437\u044b\u043a\u043e\u0432, \u043d\u043e \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043d\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c. \u0425\u043e\u0447\u0443 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043e\u0431 \u043e\u043f\u044b\u0442\u0435 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 \u0441 PHP \u043d\u0430 Zig \u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0435\u0451 \u0432 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a.  <\/p>\n<h3>\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/01b\/9e7\/de2\/01b9e7de20de111ea65663de4371840a.png\" alt=\"\u041c\u043e\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0449\u0438\u043a \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432\u0430, \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0431\u044b\u043b\u0430 \u0430\u0440\u0445\u0430\u0438\u0447\u043d\u043e\u0439: \u0440\u0430\u0441\u043f\u0430\u043a\u0443\u0439, \u0437\u0430\u043f\u0443\u0441\u0442\u0438 \u0441\u043a\u0440\u0438\u043f\u0442 \u0438 \u0442.\u0434. \u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0451 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043e\" title=\"\u041c\u043e\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0449\u0438\u043a \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432\u0430, \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0431\u044b\u043b\u0430 \u0430\u0440\u0445\u0430\u0438\u0447\u043d\u043e\u0439: \u0440\u0430\u0441\u043f\u0430\u043a\u0443\u0439, \u0437\u0430\u043f\u0443\u0441\u0442\u0438 \u0441\u043a\u0440\u0438\u043f\u0442 \u0438 \u0442.\u0434. \u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0451 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043e\" width=\"1493\" height=\"801\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/01b\/9e7\/de2\/01b9e7de20de111ea65663de4371840a.png 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/01b\/9e7\/de2\/01b9e7de20de111ea65663de4371840a.png 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u041c\u043e\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0449\u0438\u043a \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432\u0430, \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0431\u044b\u043b\u0430 \u0430\u0440\u0445\u0430\u0438\u0447\u043d\u043e\u0439: \u0440\u0430\u0441\u043f\u0430\u043a\u0443\u0439, \u0437\u0430\u043f\u0443\u0441\u0442\u0438 \u0441\u043a\u0440\u0438\u043f\u0442 \u0438 \u0442.\u0434. \u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0451 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043e<\/figcaption><\/div>\n<\/figure>\n<p>\u041f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u043c\u043d\u0435 \u0434\u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0431\u0438\u043b\u043b\u0438\u043d\u0433-\u043f\u0430\u043d\u0435\u043b\u044c \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f VPN-\u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430\u043c\u0438 \u043d\u0430 \u0431\u0430\u0437\u0435 OpenConnect (ocserv). \u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0431\u044b\u043b\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u043d\u0430 PHP \u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u043b\u0430 \u0441\u043e\u0431\u043e\u0439 \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0439 legacy-\u043a\u043e\u0434:<\/p>\n<ul>\n<li>\n<p>\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0432\u043d\u044f\u0442\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b (\u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u2014 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0445\u0430\u043e\u0441)<\/p>\n<\/li>\n<li>\n<p>\u041c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0440\u0430\u0437\u0440\u043e\u0437\u043d\u0435\u043d\u043d\u044b\u0445 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/abc\/d10\/660\/abcd106601becb3d48b6a5dbeacf4df4.jpg\" alt=\"\u041f\u0440\u0438\u043c\u0435\u0440\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438, \u0441\u0442\u0440\u0435\u043b\u043a\u0430\u043c\u0438 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043f\u043e\u0442\u043e\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\" title=\"\u041f\u0440\u0438\u043c\u0435\u0440\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438, \u0441\u0442\u0440\u0435\u043b\u043a\u0430\u043c\u0438 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043f\u043e\u0442\u043e\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445\" width=\"5098\" height=\"3753\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/abc\/d10\/660\/abcd106601becb3d48b6a5dbeacf4df4.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/abc\/d10\/660\/abcd106601becb3d48b6a5dbeacf4df4.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u041f\u0440\u0438\u043c\u0435\u0440\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438, \u0441\u0442\u0440\u0435\u043b\u043a\u0430\u043c\u0438 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043f\u043e\u0442\u043e\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/figcaption><\/div>\n<\/figure>\n<h3>\u041f\u0435\u0440\u0432\u044b\u0439 \u044d\u0442\u0430\u043f \u043c\u043e\u0434\u0435\u0440\u043d\u0438\u0437\u0430\u0446\u0438\u0438: \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u043d\u0430 Go<\/h3>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u0431\u044e\u0434\u0436\u0435\u0442\u0430 \u044f \u043f\u0440\u043e\u0432\u0435\u043b \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433:<\/p>\n<ol>\n<li>\n<p><strong>\u0412\u044b\u043d\u0435\u0441 \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043d\u0430 Go<\/strong>:<\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0440\u043e\u043a\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a<\/p>\n<\/li>\n<li>\n<p>\u0423\u0447\u0435\u0442 \u0442\u0440\u0430\u0444\u0438\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u0424\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0427\u0442\u043e \u044d\u0442\u043e \u0434\u0430\u043b\u043e<\/strong>:<\/p>\n<ul>\n<li>\n<p>10-\u043a\u0440\u0430\u0442\u043d\u044b\u0439 \u0440\u043e\u0441\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>\u0421\u043d\u0438\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u043c\u044f\u0442\u0438 \u0432 5-7 \u0440\u0430\u0437<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 24\/7 \u0431\u0435\u0437 &#171;\u043f\u0440\u043e\u0441\u0435\u0434\u0430\u043d\u0438\u0439&#187; <\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c67\/54a\/679\/c6754a679b95fb53c169edf2467cac14.jpg\" alt=\"\u041d\u043e\u0432\u0430\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f: \u0431\u0430\u043d\u043d\u0435\u0440\u044b \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432 ocserv, \u043f\u043e\u0434\u0441\u043e\u0431\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0430\u0437\u043d\u0435\u0441\u0435\u043d\u0430 \u043f\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u043c \u043d\u0430 go \u2014 \u0431\u0438\u043b\u043b\u0438\u043d\u0433 \u043f\u0430\u043d\u0435\u043b\u044c \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 UI, php \u0441\u0432\u0435\u0434\u0451\u043d \u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c\u0443 \" title=\"\u041d\u043e\u0432\u0430\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f: \u0431\u0430\u043d\u043d\u0435\u0440\u044b \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432 ocserv, \u043f\u043e\u0434\u0441\u043e\u0431\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0430\u0437\u043d\u0435\u0441\u0435\u043d\u0430 \u043f\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u043c \u043d\u0430 go \u2014 \u0431\u0438\u043b\u043b\u0438\u043d\u0433 \u043f\u0430\u043d\u0435\u043b\u044c \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 UI, php \u0441\u0432\u0435\u0434\u0451\u043d \u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c\u0443 \" width=\"5098\" height=\"3753\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/c67\/54a\/679\/c6754a679b95fb53c169edf2467cac14.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c67\/54a\/679\/c6754a679b95fb53c169edf2467cac14.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u041d\u043e\u0432\u0430\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f: \u0431\u0430\u043d\u043d\u0435\u0440\u044b \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432 ocserv, \u043f\u043e\u0434\u0441\u043e\u0431\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0430\u0437\u043d\u0435\u0441\u0435\u043d\u0430 \u043f\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u043c \u043d\u0430 go \u2014 \u0431\u0438\u043b\u043b\u0438\u043d\u0433 \u043f\u0430\u043d\u0435\u043b\u044c \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 UI, php \u0441\u0432\u0435\u0434\u0451\u043d \u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c\u0443 <\/figcaption><\/div>\n<\/figure>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u043e\u0431 \u044d\u0442\u043e\u043c \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u043d\u0435 \u0431\u0443\u0434\u0435\u043c, \u043d\u043e \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430 \u0442\u0435\u043c\u0430 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0442\u044b\u043a\u0432\u044b \u0432 \u043a\u0430\u0440\u0435\u0442\u0443 \u2014  \u0434\u0430\u0439\u0442\u0435 \u0437\u043d\u0430\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445, \u043c\u043e\u0433\u0443 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0435\u0441\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0432 \u0434\u0435\u0442\u0430\u043b\u044f\u0445 \u0438 \u043f\u043e \u0448\u0430\u0433\u0430\u043c.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439<\/h3>\n<p><strong>\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 ocserv<\/strong>: \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0436\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u0430\u0441\u044c:<\/p>\n<ul>\n<li>\n<p>\u041f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0437\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0433\u0440\u0443\u043f\u043f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e\u0431 \u043e\u0441\u0442\u0430\u0442\u043a\u0435 \u0434\u043d\u0435\u0439 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438)<\/p>\n<\/li>\n<\/ul>\n<h3>\u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0433\u0438\u0431\u043a\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0431\u0430\u043d\u043d\u0435\u0440\u043e\u0432<\/h3>\n<h4>\u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 PHP-\u0440\u0435\u0448\u0435\u043d\u0438\u0435<\/h4>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0441\u0442\u044b\u043b\u0438:<\/p>\n<ul>\n<li>\n<p>\u0422\u0440\u0435\u0431\u0443\u0435\u0442 php-fpm \u0438 \u0431\u044b\u043b\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e \u0441 PDO \u0432 \u0431\u0438\u043b\u043b\u0438\u043d\u0433 \u043f\u0430\u043d\u0435\u043b\u0438<\/p>\n<\/li>\n<li>\n<p>\u041d\u0443\u0436\u043d\u043e \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u043f\u0440\u0430\u0432\u0430\u043c\u0438 \u043d\u0430 \u0444\u0430\u0439\u043b\u044b \u0438 \u0441\u043e\u043a\u0435\u0442<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u0432\u0438\u0441\u0430\u043b\u043e<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0433\u043b\u043e \u043d\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0438\u0437-\u0437\u0430 \u043f\u043b\u043e\u0445\u043e\u0439 \u043f\u043e\u0433\u043e\u0434\u044b \u0437\u0430 \u043e\u043a\u043d\u043e\u043c<\/p>\n<\/li>\n<\/ul>\n<p>\u042f \u0431\u044b \u0441\u043a\u0430\u0437\u0430\u043b, \u0447\u0442\u043e \u044d\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0441\u0442\u0430\u0434\u0438\u0438 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0430 \u2014 \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443, \u043d\u043e \u043d\u0435 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435.<\/p>\n<h4>\u041d\u043e\u0432\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0430 Zig<\/h4>\n<p>\u042f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b:<\/p>\n<ol>\n<li>\n<p><strong>\u0421\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430<\/strong>\u00a0(\u043d\u0430 Zig):<\/p>\n<ul>\n<li>\n<p>\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u044f\u043c\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0432 ocserv<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u041f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u043d\u043e\u0432\u043e\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f<\/strong>:<\/p>\n<ul>\n<li>\n<p>\u0412\u0440\u0435\u043c\u044f \u043e\u0442\u043a\u043b\u0438\u043a\u0430: 3-5 \u043c\u0441 \u0432\u043c\u0435\u0441\u0442\u043e 50-100 \u043c\u0441<\/p>\n<\/li>\n<li>\n<p>\u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u043b\u0438\u0448\u043d\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u0441\u0431\u043e\u0435\u0432 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0437-\u0437\u0430 \u0431\u0430\u043d\u043d\u0435\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u043d\u0438\u0437\u0438\u043b\u0430\u0441\u044c \u043d\u0430 70% (\u0437\u0434\u0435\u0441\u044c \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0441 \u0443\u0447\u0451\u0442\u043e\u043c \u0438 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u043b\u043e\u0433\u0438\u043a\u0438 \u043d\u0430 go)<\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u043d\u0446\u0438\u0434\u0435\u043d\u0442\u043e\u0432 \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u043b\u043e\u0441\u044c \u043d\u0430 100%<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0442\u0430\u043a\u0443\u044e \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0438 \u043d\u0430 C, \u043d\u043e \u044d\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0432\u044b\u0439 \u0448\u0430\u0433 \u0432 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0414\u0430\u043b\u044c\u0448\u0435 \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0432 \u0432\u0438\u0434\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u0430, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043d\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432 \u043c\u043e\u0434\u0443\u043b\u0438 ocserv. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0440\u043e\u043a\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a<\/p>\n<\/li>\n<li>\n<p>\u0423\u0447\u0451\u0442 \u0442\u0440\u0430\u0444\u0438\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0438\u0445 \u0441\u0435\u0441\u0441\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>\u0418 \u0442.\u0434. <\/p>\n<\/li>\n<\/ul>\n<h2>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0438 \u0435\u0433\u043e \u043b\u043e\u0433\u0438\u043a\u0430<\/h2>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u0432\u044b \u043f\u043e\u0439\u043c\u0451\u0442\u0435, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430:<\/p>\n<ul>\n<li>\n<p>\u0412\u043e \u043f\u0435\u0440\u0432\u044b\u0445 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0441\u0430\u043c\u0438 \u043f\u043e \u0441\u0435\u0431\u0435 \u043d\u0435\u0441\u0443\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u0430\u0436\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u0432\u0440\u043e\u0434\u0435: \u2014\u0412\u0430\u0448\u0430 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 \u0438\u0441\u0442\u0435\u043a\u0430\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 2 \u0434\u043d\u044f \u0438 12 \u0447\u0430\u0441\u043e\u0432 <\/p>\n<\/li>\n<li>\n<p>\u0412\u043e \u0432\u0442\u043e\u0440\u044b\u0445, \u0432 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0438 \u043c\u0430\u043b\u0435\u0439\u0448\u0438\u0439 \u0441\u0431\u043e\u0439 \u0432 \u0441\u043a\u0440\u0438\u043f\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u0438\u043b\u0438 \u0431\u0430\u043d\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0444\u0430\u0439\u043b \u0438\u043b\u0438 \u0441\u043e\u043a\u0435\u0442) \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u2014  \u043d\u0443\u0436\u043d\u043e \u043b\u0438 \u0443\u0442\u043e\u0447\u043d\u044f\u0442\u044c, \u0447\u0442\u043e \u044d\u0442\u043e \u0443\u0440\u043e\u043d \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430?<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/520\/735\/9d5\/5207359d513b9f0e6a787f2a41049715.jpg\" alt=\"\u0423\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b: \u0435\u0441\u043b\u0438 \u0433\u0434\u0435-\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0451\u0442 \u0441\u0431\u043e\u0439, \u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043a\u043b\u044e\u0447\u0451\u043d \u0431\u0435\u0437 \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u0439 \u043f\u0440\u0438\u0447\u0438\u043d\" title=\"\u0423\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b: \u0435\u0441\u043b\u0438 \u0433\u0434\u0435-\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0451\u0442 \u0441\u0431\u043e\u0439, \u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043a\u043b\u044e\u0447\u0451\u043d \u0431\u0435\u0437 \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u0439 \u043f\u0440\u0438\u0447\u0438\u043d\" width=\"5066\" height=\"3778\" sizes=\"auto, (max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/habrastorage.org\/r\/w780\/getpro\/habr\/upload_files\/520\/735\/9d5\/5207359d513b9f0e6a787f2a41049715.jpg 780w,&#10;       https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/520\/735\/9d5\/5207359d513b9f0e6a787f2a41049715.jpg 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0423\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b: \u0435\u0441\u043b\u0438 \u0433\u0434\u0435-\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0451\u0442 \u0441\u0431\u043e\u0439, \u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043a\u043b\u044e\u0447\u0451\u043d \u0431\u0435\u0437 \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u0439 \u043f\u0440\u0438\u0447\u0438\u043d<\/figcaption><\/div>\n<\/figure>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 php-\u043a\u043e\u0434 \u043c\u043e\u0434\u0443\u043b\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  if (!true) die(\"-------- Test banner --------\");  $username = $_SERVER[\"USERNAME\"]; $site = '\/var\/www\/html\/panel'; \/\/ \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 chdir($site); require_once('include\/functions.php');  \/\/ \u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 (\u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u0434\u0430\u043d\u043e \u0438\u043b\u0438 \u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u043f\u0443\u0441\u0442\u044b\u043c) $orig_gl_message = $db-&gt;query(\"SELECT * FROM {{table}} LIMIT 1;\",\"global_message\",\"assoc\"); if (isset($orig_gl_message) &amp;&amp; $orig_gl_message['status'] == true)  $GLOBAL_MESSAGE = $orig_gl_message['message_text'];  \/\/ \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.  $GROUPS = getGroupsWithUsers();  \/\/ \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f $GROUP_MESSAGES = getGroupMessages();  \/\/ \u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 $INDIVIDUAL_MESSAGES = getIndividualMessages();  \/\/ \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u043e \u0433\u0440\u0443\u043f\u043f\u0435 function getGroupMessage($username, $groups, $groupMessages) {     foreach ($groups as $group =&gt; $users) {         if (in_array($username, $users)) {             return $groupMessages[$group]; \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u044b         }     }     return null; \/\/ \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u043f\u0440\u0438\u043d\u0430\u0434\u043b\u0435\u0436\u0438\u0442 \u043d\u0438 \u043a \u043e\u0434\u043d\u043e\u0439 \u0433\u0440\u0443\u043f\u043f\u0435 }  \/\/ \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f function getIndividualMessage($username, $individualMessages) {     return $individualMessages[$username] ?? null; \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u043e\u043d\u043e \u0435\u0441\u0442\u044c }  \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f $groupMessage = getGroupMessage($username, $GROUPS, $GROUP_MESSAGES); $individualMessage = getIndividualMessage($username, $INDIVIDUAL_MESSAGES);   $banner = \"\";  \/\/ \u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f $_username = addcslashes($username, \"\\\\'\"); $return = $db-&gt;query(\"SELECT * FROM {{table}} WHERE login='{$_username}';\", \"users\", \"assoc\");  \/\/ 1\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442: \u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 if (!empty($GLOBAL_MESSAGE)) {     $banner = $GLOBAL_MESSAGE; }  \/\/ 2\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442: \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u044b elseif ($groupMessage) {     $banner = $groupMessage; }  \/\/ 3\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442: \u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 elseif ($individualMessage) {     $banner = $individualMessage; }  \/\/ 4\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442: \u0421\u0440\u043e\u043a \u043a\u043b\u044e\u0447\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 elseif (isset($return[\"expire\"]) &amp;&amp; is_string($return[\"expire\"])) { $expire = @json_decode($return[\"expire\"], true); if(is_array($expire)) { $n = 60*60*24; $expire_timestamp = $expire[\"end\"]; \/\/ \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c timestamp \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f $expire_start_timestamp = $expire[\"start\"]; \/\/ \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c timestamp \u043d\u0430\u0447\u0430\u043b\u0430         $expire_seconds = $expire_timestamp - time(); \/\/$days = ($expire_seconds-$expire_seconds%$n)\/$n; \/\/ \u041d\u0435 \u043f\u043e\u043b\u043d\u044b\u0439 \u0434\u0435\u043d\u044c \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u043f\u043e\u043b\u043d\u044b\u0439 (n + 1) $days = floor($expire_seconds \/ $n); \/\/ \u041e\u043a\u0440\u0443\u0433\u043b\u044f\u0435\u043c \u0432\u043d\u0438\u0437 \u0434\u043e \u0446\u0435\u043b\u043e\u0433\u043e \u0447\u0438\u0441\u043b\u0430 \u0434\u043d\u0435\u0439 $hours = floor(($expire_seconds % $n) \/ (60 * 60)); \/\/ \u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u0447\u0430\u0441\u044b \u0438\u0437 \u043e\u0441\u0442\u0430\u0442\u043a\u0430 $start_date = date('d.m.Y', $expire_start_timestamp); \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u0442\u0443 \u043d\u0430\u0447\u0430\u043b\u0430 $today = date('d-m-Y'); \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0434\u0430\u0442\u0443 $expire_date = date('d.m.Y', $expire_timestamp); \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u0442\u0443 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f if ($days &gt;= 30 &amp;&amp; $days &lt; 31 ) { $messages = [ \"\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u0435\u043c! \u0412\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438 \u043b\u0443\u0447\u0448\u0435\u0435. \u0412\u0430\u0448 \u043d\u043e\u0432\u044b\u0439 \u0442\u0430\u0440\u0438\u0444 \u0443\u0436\u0435 \u0430\u043a\u0442\u0438\u0432\u0435\u043d \u0441 {$start_date} \u0433\u043e\u0434\u0430 \u0434\u043e {$expire_date} \u0433\u043e\u0434\u0430. \u041d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u0439\u0442\u0435\u0441\u044c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u043e\u043c!\", \"Sizi gutla\u00fdarys! I\u0148 gowy tarifi sa\u00fdlady\u0148yz. T\u00e4ze tarifi\u0148iz {$start_date} -- {$expire_date} senesi i\u015fje\u0148dir. Howpsuz we \u00e7alt internetden lezzet aly\u0148!\"     ]; $banner .= $messages[array_rand($messages)];  } elseif ($days &gt;= 6 &amp;&amp; $days &lt; 7) { $messages = [ \"\u0423\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u043c \u0432\u0430\u0441, \u0447\u0442\u043e \u0432\u0430\u0448 \u0442\u0430\u0440\u0438\u0444\u043d\u044b\u0439 \u043f\u043b\u0430\u043d \u0430\u043a\u0442\u0438\u0432\u0435\u043d \u0432 \u043f\u0435\u0440\u0438\u043e\u0434 \u0441 {$start_date} \u043f\u043e {$expire_date}. \u0416\u0435\u043b\u0430\u0435\u043c \u0445\u043e\u0440\u043e\u0448\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f!\", \"Size {$start_date} -- {$expire_date} seneleri aralygynda tarif planynyzy\u0148 i\u015fje\u0148digi barada habar ber\u00fd\u00e4ris. Ulany\u015fy\u0148yz rahat bolsun!\"      ]; $banner .= $messages[array_rand($messages)];            } elseif ($days &gt;= 1 &amp;&amp; $days &lt; 2) { $messages = [ \"\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435! \u0421\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432\u0430\u0448\u0435\u0433\u043e VPN-\u043a\u043b\u044e\u0447\u0430 \u0438\u0441\u0442\u0435\u043a\u0430\u0435\u0442 {$expire_date}\u0433\u043e\u0434\u0430. \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0434\u043d\u0435\u0439: {$days} \u0438 \u0447\u0430\u0441\u043e\u0432: {$hours}.\", \"Hormatly agzamyz! Sizin VPN a\u00e7ary\u0148yzy\u0148 m\u00f6hleti {$expire_date} senesinde gutar\u00fdar. M\u00f6hletine {$days} g\u00fcn we {$hours} sagat galdy.\"     ]; $banner .= $messages[array_rand($messages)];                }elseif($days &gt;= 0 &amp;&amp; $days &lt; 1) { $messages = [         \"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435! \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0447\u0430\u0441\u043e\u0432: {$hours} \u0434\u043e \u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u044f \u0441\u0440\u043e\u043a\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432\u0430\u0448\u0435\u0433\u043e VPN-\u043a\u043b\u044e\u0447\u0430. \u041f\u0435\u0440\u0438\u043e\u0434 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f: \u0441 {$start_date}\u0433 \u043f\u043e {$expire_date}\u0433.\",         \"\u00dcns beri\u0148! VPN a\u00e7ary\u0148yzy\u0148 m\u00f6hletini\u0148 gutarmagyna {$hours} sagat galdy. M\u00f6hleti: {$start_date}-den {$expire_date}-e \u00e7enli.\"         ];     $banner .= $messages[array_rand($messages)]; }elseif($days &gt;= -3 &amp;&amp; $days &lt;= 0) { $messages = [         \"\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u0430\u0448 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d \u0438\u0437-\u0437\u0430 \u043d\u0443\u043b\u0435\u0432\u043e\u0433\u043e \u0431\u0430\u043b\u0430\u043d\u0441\u0430.\",         \"Bagy\u015fla\u0148, h\u00e4zirki wagtda balansy\u0148yz nol bolany \u00fc\u00e7in hasaby\u0148yz bloklanyldy. \"         ];     $banner .= $messages[array_rand($messages)]; } } }  echo($banner); exit(0);<\/code><\/pre>\n<\/div>\n<\/details>\n<p><strong>\u041a\u0430\u043a \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e \u0432 \u043f\u043b\u0430\u043d\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 php \u0432 ocserv<\/strong><\/p>\n<p>\u0412 \u043f\u0430\u043f\u043a\u0443 \u0441 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c\u0438 \u043a\u043e\u0434\u0430\u043c\u0438 ocserv, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f C-\u0445\u0438\u0434\u0435\u0440 <strong>fpm-client.h<\/strong>: <\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">#include &lt;error.h&gt; #include &lt;errno.h&gt; #include &lt;sys\/socket.h&gt; #include &lt;sys\/un.h&gt; #include &lt;sys\/stat.h&gt; #include &lt;fcntl.h&gt; #include &lt;unistd.h&gt; #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt;  #include &lt;fastcgi.h&gt;  int fpm_client(char *fpm_socket_file_path, char *result, char *username) { int ret, len;  int unix_stream_socket; unix_stream_socket = socket(AF_UNIX, SOCK_STREAM, 0); if(unix_stream_socket &lt; 0) { error(0, errno, \"Can't create unix socket stream\"); return(-1); }  struct sockaddr_un addr; memset(&amp;addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; len = strlen(fpm_socket_file_path); memcpy(addr.sun_path, fpm_socket_file_path, len+1);  ret = connect(unix_stream_socket, (const struct sockaddr *) &amp;addr, sizeof(addr)); if(ret &lt; 0){ error(0, errno, \"Can't connect to unix socket stream\"); return(-1); }   FCGI_Header header; header.version = FCGI_VERSION_1; header.requestIdB1 = 0; header.requestIdB0 = 1; header.paddingLength = 0;  \/\/ FCGI_BEGIN_REQUEST  header.type = FCGI_BEGIN_REQUEST;  FCGI_BeginRequestBody begin_request_body; begin_request_body.roleB1 = 0; begin_request_body.roleB0 = FCGI_RESPONDER; begin_request_body.flags = 0;  len = sizeof(begin_request_body); header.contentLengthB1 = 0; header.contentLengthB0 = len;  ret = write(unix_stream_socket, &amp;header, sizeof(header)); if(ret &lt; 0) { error(0, errno, \"Can't write `begin request header`\"); return(-1); }  ret = write(unix_stream_socket, &amp;begin_request_body, len); if(ret &lt; 0) { error(0, errno, \"Can't write `begin request body`\"); return(-1); }  \/\/ FCGI_PARAMS  header.type = FCGI_PARAMS;  char *params[] = { \"SCRIPT_FILENAME\", \"\/etc\/ocserv\/banner.php\", \"REQUEST_METHOD\", \"GET\", \"USERNAME\", \"user\" }; params[5] = username;  int i, c, nl, vl; char buffer[65536]; c = sizeof(params)\/sizeof(params[0]); len = 0; for(i = 0; i &lt; c; i += 2) { nl = strlen(params[i]); vl = strlen(params[i+1]);  memcpy(buffer+len, &amp;nl, 1); len += 1;  memcpy(buffer+len, &amp;vl, 1); len += 1;  memcpy(buffer+len, params[i], nl); len += nl;  memcpy(buffer+len, params[i+1], vl); len += vl; }  header.contentLengthB1 = (len &gt;&gt;8) &amp; 0xff; header.contentLengthB0 = len &amp; 0xff;  ret = write(unix_stream_socket, &amp;header, sizeof(header)); if(ret &lt; 0) { error(0, errno, \"Can't write `params request header`\"); return(-1); }  ret = write(unix_stream_socket, buffer, len); if(ret &lt; 0) { error(0, errno, \"Can't write `params request body`\"); return(-1); }  header.contentLengthB1 = 0; header.contentLengthB0 = 0;  ret = write(unix_stream_socket, &amp;header, sizeof(header)); if(ret &lt; 0) { error(0, errno, \"Can't write `empty params request header`\"); return(-1); }  \/\/ FCGI_STDOUT  ret = read(unix_stream_socket, &amp;header, 8); if(ret &lt; 0) { error(0, errno, \"Can't read stdout response header\"); return(-1); }  len = 0; len = header.contentLengthB1 &lt;&lt; 8; len = len | header.contentLengthB0;  memset(buffer, 0, 65536);  ret = read(unix_stream_socket, buffer, len); if(ret &lt; 0) { error(0, errno, \"Can't read stdout response body\"); return(-1); }  \/\/write(1, buffer, len);  char *offset; offset = strstr(buffer, \"\\r\\n\\r\\n\"); offset += 4;  \/\/printf(\"\\n%s\\n\", offset);  len = len-(offset-buffer); memcpy(result, offset, len);  ret = close(unix_stream_socket); if(ret &lt; 0) { error(0, errno, \"Can't close unix socket stream\"); return(-1); }  return(0); } <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u043f\u0440\u0430\u0432\u043a\u0438 \u0432 \u0444\u0430\u0439\u043b <strong>worker-auth.c:<\/strong><\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043f\u0430\u0442\u0447 (\u0437\u0434\u0435\u0441\u044c \u0434\u043b\u044f \u0432\u0435\u0440\u0441\u0438\u0438 <strong>ocserv 0.12.6<\/strong>, \u0432 \u0434\u0440\u0443\u0433\u0438\u0445 \u0432\u0435\u0440\u0441\u0438\u044f \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c\u0441\u044f):<\/p>\n<pre><code class=\"cpp\">46a47,48 &gt; #include \"fpm_client.h\" &gt;  995a998,1021 &gt;   char *fpm_socket_file_path = \"\/var\/run\/php\/php-fpm.sock\\0\";    char banner[256], username[32];  memset(banner, 0, 256);  memset(username, 0, 32);    int len;  len = strlen(ws-&gt;username);  memcpy(username, ws-&gt;username, len);    ret = fpm_client(fpm_socket_file_path, banner, username);  \/*  int fd = open(\"\/tmp\/ocserv.log\", O_WRONLY);  char str[256];  sprintf(str, \"\\nret = %d, banner = %s\", ret, banner);  len = strlen(str);  write(fd, str, len);  close(fd);  *\/    len = strlen(banner);  memcpy(WSCONFIG(ws)-&gt;banner, banner, len+1); &gt; <\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043f\u0430\u0442\u0447 \u0432\u043a\u0430\u0442\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u044e <strong>int post_common_handler(worker_st <em> ws, unsigned http_ver, const char <\/em>imsg)<\/strong><\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u043e\u043c<\/p>\n<pre><code class=\"cpp\">if (WSCONFIG(ws)-&gt;banner) { size =     snprintf(msg, sizeof(msg), \"&lt;banner&gt;%s&lt;\/banner&gt;\",      WSCONFIG(ws)-&gt;banner); if (size &lt;= 0) goto fail; \/* snprintf() returns not a very useful value, so we need to recalculate *\/ size = strlen(msg); } else { msg[0] = 0; size = 0; }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0414\u0430\u043b\u044c\u0448\u0435 \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043e\u0431\u0440\u0430\u0442\u044c ocserv, \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c ocserv \u0438 ocserv-worker \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u0441 php-fpm \u0434\u043b\u044f \u0432\u044b\u0437\u043e\u0432\u0430 \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u0431\u0430\u043d\u043d\u0435\u0440\u0430.<\/p>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u043c\u044b \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u043c \u043a \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c\u0443 \u043d\u0430\u0440\u0440\u0430\u0442\u0438\u0432\u0443 \u0441\u0442\u0430\u0442\u044c\u0438: \u043a\u0430\u043a \u0432\u044b\u0437\u0432\u0430\u0442\u044c Zig-\u043a\u043e\u0434 \u0438\u0437 C. \u0412 \u0433\u0443\u0433\u043b\u0435 \u043f\u043e\u043b\u043d\u043e \u0441\u0442\u0430\u0442\u0435\u0439 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0432\u044b\u0437\u0432\u0430\u0442\u044c C \u0438\u0437 Zig, \u0447\u0435\u0433\u043e \u043d\u0435 \u0441\u043a\u0430\u0436\u0435\u0448\u044c \u043e\u0431 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435. \u042d\u0442\u043e \u0438 \u043f\u043e\u0431\u0443\u0434\u0438\u043b\u043e \u043c\u0435\u043d\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0442\u0430\u0442\u044c\u044e.<\/p>\n<h2>\u041d\u043e\u0432\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0430 Zig: \u043b\u043e\u0433\u0438\u043a\u0430 \u0438 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/h2>\n<p>\u0422\u0430\u043a \u0436\u0435 \u0437\u0434\u0435\u0441\u044c \u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e: \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043d\u0430 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0435 IP \u0430\u0434\u0435\u0440\u0441\u0430 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043e\u043c\u0443 \u043f\u0440\u043e\u043a\u0441\u0438-\u0441\u0435\u0440\u0432\u0435\u0440\u0443.<\/p>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0447\u0435\u0440\u0435\u0437 \u043f\u0440\u043e\u043a\u0441\u0438-\u043f\u0440\u043e\u0441\u043b\u043e\u0439\u043a\u0443, \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0435\u0441\u043b\u0438 \u043a\u043b\u0438\u0435\u043d\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u043d\u0435 \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u043a\u0441\u0438 \u0438\u043b\u0438 \u0432 \u043e\u0431\u0445\u043e\u0434 \u043d\u0435\u0433\u043e \u2014 \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438 \u0432\u044b\u0432\u0435\u0441\u0442\u0438 \u0431\u0430\u043d\u043d\u0435\u0440, \u043e \u0442\u043e\u043c \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u0437\u0430\u043f\u0440\u0435\u0449\u0451\u043d. \u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0431\u044b\u043b\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043e \u0432 status-daemon \u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e, \u0430 \u0432\u043e\u0442 \u0431\u0430\u043d\u043d\u0435\u0440 \u043d\u0435 \u0432\u044b\u0432\u043e\u0434\u0438\u043b\u0441\u044f, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u043b\u0430 \u0432 \u0441\u043a\u0440\u0438\u043f\u0442 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u043e\u0431 IP \u0430\u0434\u0440\u0435\u0441\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f. <\/p>\n<p>\u0412 \u043d\u043e\u0432\u043e\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0438 \u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u044d\u0442\u043e\u0442 \u0431\u0430\u043d\u043d\u0435\u0440, \u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0437\u0430 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u043e\u0441\u0442\u0430\u0432\u0438\u043b \u043a\u0430\u043a \u0435\u0441\u0442\u044c \u2014 \u0437\u0430\u0447\u0435\u043c \u0442\u0440\u043e\u0433\u0430\u0442\u044c \u0442\u043e, \u0447\u0442\u043e \u0438 \u0442\u0430\u043a \u0445\u043e\u0440\u043e\u0448\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442?<\/p>\n<p>\u0421\u0430\u043c\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u0435\u043b\u0430\u043b\u043e\u0441\u044c \u0432 3 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u0441\u0431\u043e\u0440\u043e\u0447\u043d\u043e\u043c \u0444\u0430\u0439\u043b\u0435 \u0432\u044b\u0448\u043b\u043e 2 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0441\u0431\u043e\u0440\u043a\u0438:<\/p>\n<ul>\n<li>\n<p>\u0421\u0431\u043e\u0440\u043a\u0430 \u043e\u0431\u044b\u0447\u043d\u043e\u0433\u043e \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0430 \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0435\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0430 \u0432 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0445 \u0442\u0435\u0441\u0442\u043e\u0432<\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 PostgreSQL \u0432\u044b\u0431\u0440\u0430\u043b <a href=\"https:\/\/github.com\/karlseguin\/pg.zig\" rel=\"noopener noreferrer nofollow\">pg.zig<\/a>, \u043d\u0430\u0442\u0438\u0432\u043d\u044b\u0439 \u0434\u0440\u0430\u0439\u0432\u0435\u0440. <\/p>\n<pre><code class=\"cpp\">var pool = try pg.Pool.init(allocator, .{     .size = 5,     .connect = .{.host = config.host, .port = config.port},     .auth = .{         .username = config.username,         .password = config.password,         .database = config.database,     }, });<\/code><\/pre>\n<p><em>\u041d\u0430 \u0425\u0430\u0431\u0440\u0435 \u043d\u0435\u0442 \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u043a\u0438 Zig, \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c C++ \u0447\u0442\u043e\u0431\u044b \u0445\u043e\u0442\u044c \u043a\u0430\u043a-\u0442\u043e \u043f\u043e\u0434\u0441\u0432\u0435\u0442\u0438\u0442\u044c \u0441\u0438\u043d\u0442\u0430\u043a\u0441\u0438\u0441.<\/em><\/p>\n<p>\u041d\u0430 \u0432\u0441\u044f\u043a\u0438\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 \u0441\u0434\u0435\u043b\u0430\u043b \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0447\u0435\u0440\u0435\u0437 toml-\u0444\u0430\u0439\u043b, \u043f\u043e \u0431\u043e\u043b\u044c\u0448\u0435\u043c\u0443 \u0441\u0447\u0451\u0442\u0443 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u044d\u0442\u043e \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f. <\/p>\n<pre><code>host = \"\" port = 5432 username = \"\" password = \"\" database = \"\" pool_size = 5 timeout_ms = 10000<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 toml-\u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u0443 \u043c\u0435\u043d\u044f \u043a\u0440\u0430\u0439\u043d\u0435 \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u0430\u044f, \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u043b\u0441\u044f \u0441\u0430\u043c\u043e\u043f\u0438\u0441\u043d\u044b\u043c \u043f\u0430\u0440\u0441\u0435\u0440\u043e\u043c.<\/p>\n<pre><code class=\"cpp\">const Config = struct {     host: []const u8 = \"localhost\",     port: u16 = 5432,     \/\/ ... };  fn parseToml(allocator: std.mem.Allocator, data: []const u8) !Config {     \/\/ \u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u0440\u0443\u0447\u043d\u043e\u0439 \u043f\u0430\u0440\u0441\u0438\u043d\u0433 }<\/code><\/pre>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u0431\u0430\u043d\u043d\u0435\u0440\u043e\u0432: \u043f\u0440\u044f\u043c\u043e\u0439 \u043f\u043e\u0440\u0442 \u0441 PHP, \u043d\u043e \u0431\u0435\u0437 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0442\u0438\u043f\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<pre><code class=\"cpp\">fn getBanner(allocator: std.mem.Allocator, pool: *Pool, login: []const u8) !?Banner {     \/\/ 1. \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439     \/\/ 2. \u041f\u043e\u0438\u0441\u043a \u0432 \u0433\u0440\u0443\u043f\u043f\u0430\u0445     \/\/ 3. \u041f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0435 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f     \/\/ 4. \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0440\u043e\u043a\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f }<\/code><\/pre>\n<h4>\u041a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b \u0432 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0435 \u0441 PHP<\/h4>\n<p><strong>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u043f\u0430\u043c\u044f\u0442\u044c\u044e<\/strong><\/p>\n<p>PHP:<\/p>\n<pre><code class=\"php\">\/\/ \u0412\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e \u2014 \u0432\u044b\u0434\u0435\u043b\u0438\u043b\u0438 \u0438 \u0437\u0430\u0431\u044b\u043b\u0438 $message = loadMessageFromDB();<\/code><\/pre>\n<p>Zig:<\/p>\n<pre><code class=\"cpp\">\/\/ \u042f\u0432\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u044c\u044e const message = try allocator.dupe(u8, db_message); defer allocator.free(message);<\/code><\/pre>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b \u0441\u0442\u0440\u0430\u0442\u0435\u0433\u0438\u044e:<\/p>\n<ul>\n<li>\n<p>\u0410\u0440\u0435\u043d\u0430-\u0430\u043b\u043b\u043e\u043a\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u043a\u0440\u0430\u0442\u043a\u043e\u0441\u0440\u043e\u0447\u043d\u044b\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0449\u0438\u0439 \u043f\u0443\u043b \u0434\u043b\u044f \u0434\u043e\u043b\u0433\u043e\u0436\u0438\u0432\u0443\u0449\u0438\u0445 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440<\/p>\n<\/li>\n<li>\n<p>\u0414\u0435\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0432 \u0442\u0435\u0441\u0442\u0430\u0445<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 PostgreSQL<\/strong><\/p>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u043f\u0440\u0438\u0432\u044b\u0447\u043d\u043e\u0433\u043e PDO:<\/p>\n<pre><code class=\"cpp\">var result = try pool.query(     \\\\SELECT * FROM messages WHERE active = true , .{}); defer result.deinit();  while (try result.next()) |row| {     const id = row.get(i32, 0);     \/\/ ... }<\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b:<\/p>\n<ul>\n<li>\n<p>\u041f\u0443\u043b\u043b\u0438\u043d\u0433 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>\u0411\u0430\u0442\u0447\u0438\u043d\u0433 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>Zero-copy \u043f\u0430\u0440\u0441\u0438\u043d\u0433 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a<\/strong><\/p>\n<pre><code class=\"cpp\">const maybe_message = getBannerMessage(allocator, pool, login) catch |err| {         std.log.err(\"Failed to get banner message: {}\", .{err});         return false; };<\/code><\/pre>\n<p>\u041d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 \u0441\u0432\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u043e\u0448\u0438\u0431\u043a\u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u0432 ocserv \u0438 \u043f\u043e\u043f\u0430\u0434\u0430\u044e\u0442 \u0432 \u0435\u0433\u043e \u043b\u043e\u0433.<\/p>\n<h4>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043c\u043e\u0434\u0443\u043b\u044f<\/h4>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u0435\u0440\u0432\u043e\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0434\u0443\u043b\u044f \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438, \u0432 \u0432\u0438\u0434\u0435 \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u043e\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b:<\/p>\n<details class=\"spoiler\">\n<summary>main.zig<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">const std = @import(\"std\"); const pg = @import(\"pg\");  \/\/ \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a \u0411\u0414 const Config = struct {     host: []const u8 = \"127.0.0.1\",     port: u16 = 5432,     username: []const u8 = \"postgres\",     password: []const u8 = \"postgres\",     database: []const u8 = \"postgres\",     pool_size: u16 = 5,     timeout_ms: u32 = 10_000, };  \/\/ \u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u044b \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 const MessagePriority = enum {     global,     group,     individual,     expire, };  \/\/ \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f const BannerMessage = struct {     text: []const u8, \/\/ \u0422\u0435\u043a\u0441\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043a\u0443\u0447\u0435)     priority: MessagePriority, \/\/ \u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f };  \/\/ \u0414\u0430\u043d\u043d\u044b\u0435 \u043e \u0441\u0440\u043e\u043a\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f const ExpireData = struct {     start_timestamp: i64, \/\/ \u0412\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043c\u0435\u0442\u043a\u0430 \u043d\u0430\u0447\u0430\u043b\u0430     end_timestamp: i64, \/\/ \u0412\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043c\u0435\u0442\u043a\u0430 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f     start_date_str: []const u8, \/\/ \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0434\u0430\u0442\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043a\u0443\u0447\u0435)     end_date_str: []const u8, \/\/ \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0434\u0430\u0442\u0430 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0432 \u043a\u0443\u0447\u0435) };  \/\/\/ \u041f\u0430\u0440\u0441\u0438\u043d\u0433 TOML \u043a\u043e\u043d\u0444\u0438\u0433\u0430 (\u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f) \/\/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u0442\u0440\u043e\u043a\u043e\u0432\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0444\u0438\u0433\u0430 \/\/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u043f\u0440\u0438 \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0445 \u0447\u0438\u0441\u043b\u043e\u0432\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u0445 fn parseToml(allocator: std.mem.Allocator, data: []const u8) !Config {     var config = Config{};     var lines = std.mem.tokenizeSequence(u8, data, \"\\n\");      while (lines.next()) |line| {         const trimmed = std.mem.trim(u8, line, \" \\t\");         \/\/ \u041f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u0443\u0441\u0442\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438         if (trimmed.len == 0 or trimmed[0] == '#') continue;          if (std.mem.indexOf(u8, trimmed, \"=\")) |eq_pos| {             const key = std.mem.trim(u8, trimmed[0..eq_pos], \" \\t\\\"'\");             const value = std.mem.trim(u8, trimmed[eq_pos + 1 ..], \" \\t\\\"'\");              \/\/ \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043a\u0430\u0436\u0434\u043e\u0435 \u043f\u043e\u043b\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0430             if (std.mem.eql(u8, key, \"host\")) {                 \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u0442\u0440\u043e\u043a\u0443 \u0445\u043e\u0441\u0442\u0430                 config.host = try allocator.dupe(u8, value);             } else if (std.mem.eql(u8, key, \"port\")) {                 \/\/ \u041f\u0430\u0440\u0441\u0438\u043c \u0447\u0438\u0441\u043b\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u043e\u0440\u0442\u0430                 config.port = try std.fmt.parseInt(u16, value, 10);             } else if (std.mem.eql(u8, key, \"username\")) {                 \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f                 config.username = try allocator.dupe(u8, value);             } else if (std.mem.eql(u8, key, \"password\")) {                 \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u043f\u0430\u0440\u043e\u043b\u044c                 config.password = try allocator.dupe(u8, value);             } else if (std.mem.eql(u8, key, \"database\")) {                 \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0438\u043c\u044f \u0411\u0414                 config.database = try allocator.dupe(u8, value);             } else if (std.mem.eql(u8, key, \"pool_size\")) {                 \/\/ \u041f\u0430\u0440\u0441\u0438\u043c \u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u0443\u043b\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439                 config.pool_size = try std.fmt.parseInt(u16, value, 10);             } else if (std.mem.eql(u8, key, \"timeout_ms\")) {                 \/\/ \u041f\u0430\u0440\u0441\u0438\u043c \u0442\u0430\u0439\u043c\u0430\u0443\u0442                 config.timeout_ms = try std.fmt.parseInt(u32, value, 10);             }         }     }      return config; }  \/\/\/ \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \/\/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0442\u0435\u043a\u0441\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \/\/\/ \u0412\u0430\u0436\u043d\u043e: \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0430\u044f \u0441\u0442\u043e\u0440\u043e\u043d\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0442\u044c \u043f\u0430\u043c\u044f\u0442\u044c text \u0432 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u043e\u043c BannerMessage fn getBannerMessage(     allocator: std.mem.Allocator,     pool: *pg.Pool,     login: []const u8, ) !?BannerMessage {     \/\/ 1. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435     {         \/\/ \u0412\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u0411\u0414 (\u043f\u0430\u043c\u044f\u0442\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0443\u043b\u043e\u043c)         var global_result = try pool.query(             \\\\SELECT message_text FROM global_message WHERE status = true LIMIT 1         , .{});         defer global_result.deinit(); \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430          if (try global_result.next()) |row| {             const message = row.get([]const u8, 0);             \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u043a\u043e\u043f\u0438\u044e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f             return BannerMessage{                 .text = try allocator.dupe(u8, message),                 .priority = MessagePriority.global,             };         }     }      \/\/ 2. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0433\u0440\u0443\u043f\u043f\u044b \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f     \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0433\u0440\u0443\u043f\u043f (\u043d\u0443\u0436\u043d\u043e \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0442\u044c)     var groups = try getGroupsWithUsers(allocator, pool);     defer {         \/\/ \u0420\u0443\u0447\u043d\u043e\u0435 \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445         var it = groups.iterator();         while (it.next()) |entry| {             \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0433\u0440\u0443\u043f\u043f\u0435             for (entry.value_ptr.items) |user| {                 allocator.free(user);             }             entry.value_ptr.deinit(); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c ArrayList             allocator.free(entry.key_ptr.*); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f\u044b         }         groups.deinit(); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0445\u0435\u0448-\u043c\u0430\u043f     }      \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f (\u043d\u0443\u0436\u043d\u043e \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0442\u044c)     var group_messages = try getGroupMessages(allocator, pool);     defer {         \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0445\u0435\u0448-\u043c\u0430\u043f \u0441 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u0433\u0440\u0443\u043f\u043f         var it = group_messages.iterator();         while (it.next()) |entry| {             allocator.free(entry.key_ptr.*); \/\/ \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f\u044b             allocator.free(entry.value_ptr.*); \/\/ \u0422\u0435\u043a\u0441\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f         }         group_messages.deinit();     }      \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f\u044b     if (findGroupForUser(login, &amp;groups)) |group_name| {         if (group_messages.get(group_name)) |message| {             \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u043a\u043e\u043f\u0438\u044e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f             return BannerMessage{                 .text = try allocator.dupe(u8, message),                 .priority = MessagePriority.group,             };         }     }      \/\/ 3. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f (\u043d\u0443\u0436\u043d\u043e \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0442\u044c)     var individual_messages = try getIndividualMessages(allocator, pool);     defer {         \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0445\u0435\u0448-\u043c\u0430\u043f \u0441 \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438         var it = individual_messages.iterator();         while (it.next()) |entry| {             allocator.free(entry.key_ptr.*); \/\/ \u041b\u043e\u0433\u0438\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f             allocator.free(entry.value_ptr.*); \/\/ \u0422\u0435\u043a\u0441\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f         }         individual_messages.deinit();     }      if (individual_messages.get(login)) |message| {         \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u043a\u043e\u043f\u0438\u044e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f         return BannerMessage{             .text = try allocator.dupe(u8, message),             .priority = MessagePriority.individual,         };     }      \/\/ 4. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043a\u043b\u044e\u0447\u0430     {         var user_result = try pool.query(             \\\\SELECT expire FROM users WHERE login = $1 LIMIT 1         , .{login});         defer user_result.deinit(); \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435          if (try user_result.next()) |row| {             const expire_json = row.get(?[]const u8, 0);             if (expire_json) |json| {                 const now = std.time.timestamp();                 \/\/ \u041f\u0430\u0440\u0441\u0438\u043c JSON (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u0430\u0442)                 const expire = try parseExpireJson(allocator, json);                 defer {                     \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u0430\u0442                     allocator.free(expire.start_date_str);                     allocator.free(expire.end_date_str);                 }                  \/\/ \u0420\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c \u043e\u0441\u0442\u0430\u0432\u0448\u0435\u0435\u0441\u044f \u0432\u0440\u0435\u043c\u044f                 const seconds_left = expire.end_timestamp - now;                 const days_left = @divTrunc(seconds_left, 86400);                 const hours_left = @divTrunc(@mod(seconds_left, 86400), 3600);                  \/\/ \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043e\u0441\u0442\u0430\u0432\u0448\u0435\u0433\u043e\u0441\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u0438                 if (days_left &gt;= 30 and days_left &lt; 31) {                     const msg1 = try std.fmt.allocPrint(allocator, \"\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u0435\u043c! \u0412\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438 \u043b\u0443\u0447\u0448\u0435\u0435. \u0412\u0430\u0448 \u043d\u043e\u0432\u044b\u0439 \u0442\u0430\u0440\u0438\u0444 \u0443\u0436\u0435 \u0430\u043a\u0442\u0438\u0432\u0435\u043d \u0441 {s} \u0433\u043e\u0434\u0430 \u0434\u043e {s} \u0433\u043e\u0434\u0430. \u041d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u0439\u0442\u0435\u0441\u044c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u043e\u043c!\", .{ expire.start_date_str, expire.end_date_str });                     defer allocator.free(msg1);                      const msg2 = try std.fmt.allocPrint(allocator, \"Sizi gutla\u00fdarys! I\u0148 gowy tarifi sa\u00fdlady\u0148yz. T\u00e4ze tarifi\u0148iz {s} -- {s} senesi i\u015fje\u0148dir. Howpsuz we \u00e7alt internetden lezzet aly\u0148!\", .{ expire.start_date_str, expire.end_date_str });                     defer allocator.free(msg2);                      const chosen = if (std.crypto.random.int(u1) == 0) msg1 else msg2;                     return BannerMessage{                         .text = try allocator.dupe(u8, chosen),                         .priority = MessagePriority.expire,                     };                 } else if (days_left &gt;= 6 and days_left &lt; 7) {                     const msg1 = try std.fmt.allocPrint(allocator, \"\u0423\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u043c \u0432\u0430\u0441, \u0447\u0442\u043e \u0432\u0430\u0448 \u0442\u0430\u0440\u0438\u0444\u043d\u044b\u0439 \u043f\u043b\u0430\u043d \u0430\u043a\u0442\u0438\u0432\u0435\u043d \u0432 \u043f\u0435\u0440\u0438\u043e\u0434 \u0441 {s} \u043f\u043e {s}. \u0416\u0435\u043b\u0430\u0435\u043c \u0445\u043e\u0440\u043e\u0448\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f!\", .{ expire.start_date_str, expire.end_date_str });                     defer allocator.free(msg1);                      const msg2 = try std.fmt.allocPrint(allocator, \"Size {s} -- {s} seneleri aralygynda tarif planynyzy\u0148 i\u015fje\u0148digi barada habar ber\u00fd\u00e4ris. Ulany\u015fy\u0148yz rahat bolsun!\", .{ expire.start_date_str, expire.end_date_str });                     defer allocator.free(msg2);                      const chosen = if (std.crypto.random.int(u1) == 0) msg1 else msg2;                     return BannerMessage{                         .text = try allocator.dupe(u8, chosen),                         .priority = MessagePriority.expire,                     };                 } else if (days_left &gt;= 1 and days_left &lt; 2) {                     const msg1 = try std.fmt.allocPrint(allocator, \"\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435! \u0421\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432\u0430\u0448\u0435\u0433\u043e VPN-\u043a\u043b\u044e\u0447\u0430 \u0438\u0441\u0442\u0435\u043a\u0430\u0435\u0442 {s} \u0433\u043e\u0434\u0430. \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0434\u043d\u0435\u0439: {} \u0438 \u0447\u0430\u0441\u043e\u0432: {}.\", .{ expire.end_date_str, days_left, hours_left });                     defer allocator.free(msg1);                      const msg2 = try std.fmt.allocPrint(allocator, \"Hormatly agzamyz! Sizin VPN a\u00e7ary\u0148yzy\u0148 m\u00f6hleti {s} senesinde gutar\u00fdar. M\u00f6hletine {} g\u00fcn we {} sagat galdy.\", .{ expire.end_date_str, days_left, hours_left });                     defer allocator.free(msg2);                      const chosen = if (std.crypto.random.int(u1) == 0) msg1 else msg2;                     return BannerMessage{                         .text = try allocator.dupe(u8, chosen),                         .priority = MessagePriority.expire,                     };                 } else if (days_left &gt;= 0 and days_left &lt; 1) {                     const msg1 = try std.fmt.allocPrint(allocator, \"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435! \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0447\u0430\u0441\u043e\u0432: {} \u0434\u043e \u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u044f \u0441\u0440\u043e\u043a\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432\u0430\u0448\u0435\u0433\u043e VPN-\u043a\u043b\u044e\u0447\u0430. \u041f\u0435\u0440\u0438\u043e\u0434 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f: \u0441 {s}\u0433 \u043f\u043e {s}\u0433.\", .{ hours_left, expire.start_date_str, expire.end_date_str });                     defer allocator.free(msg1);                      const msg2 = try std.fmt.allocPrint(allocator, \"\u00dcns beri\u0148! VPN a\u00e7ary\u0148yzy\u0148 m\u00f6hletini\u0148 gutarmagyna {} sagat galdy. M\u00f6hleti: {s}-den {s}-e \u00e7enli.\", .{ hours_left, expire.start_date_str, expire.end_date_str });                     defer allocator.free(msg2);                      const chosen = if (std.crypto.random.int(u1) == 0) msg1 else msg2;                     return BannerMessage{                         .text = try allocator.dupe(u8, chosen),                         .priority = MessagePriority.expire,                     };                 } else if (days_left &gt;= -3 and days_left &lt; 0) {                     const msg1: []const u8 = \"\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u0430\u0448 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d \u0438\u0437-\u0437\u0430 \u043d\u0443\u043b\u0435\u0432\u043e\u0433\u043e \u0431\u0430\u043b\u0430\u043d\u0441\u0430.\";                      const msg2: []const u8 = \"Bagy\u015fla\u0148, h\u00e4zirki wagtda balansy\u0148yz nol bolany \u00fc\u00e7in hasaby\u0148yz bloklanyldy.\";                      const chosen = if (std.crypto.random.int(u1) == 0) msg1 else msg2;                     return BannerMessage{                         .text = try allocator.dupe(u8, chosen),                         .priority = MessagePriority.expire,                     };                 }             }         }     }      return null; \/\/ \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e }  \/\/\/ \u041f\u0430\u0440\u0441\u0438\u043d\u0433 JSON \u0441 \u0434\u0430\u0442\u0430\u043c\u0438 \u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u044f \/\/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u0442\u0440\u043e\u043a\u0438 \u0434\u0430\u0442 fn parseExpireJson(allocator: std.mem.Allocator, json_str: []const u8) !ExpireData {     \/\/ \u041f\u0430\u0440\u0441\u0438\u043c JSON (\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e \u043f\u0430\u043c\u044f\u0442\u044c \u0438\u0437 \u0430\u043b\u043b\u043e\u043a\u0430\u0442\u043e\u0440\u0430)     const parsed = try std.json.parseFromSlice(struct {         start: i64,         end: i64,     }, allocator, json_str, .{});     defer parsed.deinit(); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u043f\u0430\u0440\u0441\u0435\u0440\u0430      const start_timestamp = parsed.value.start;     const end_timestamp = parsed.value.end;      \/\/ \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u0442\u044b (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u0442\u0440\u043e\u043a\u0438)     const start_date_str = try formatTimestamp(allocator, start_timestamp);     const end_date_str = try formatTimestamp(allocator, end_timestamp);      return ExpireData{         .start_timestamp = start_timestamp,         .end_timestamp = end_timestamp,         .start_date_str = start_date_str,         .end_date_str = end_date_str,     }; }  \/\/\/ \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043c\u0435\u0442\u043a\u0438 \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 \/\/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0438\u0440\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 fn formatTimestamp(allocator: std.mem.Allocator, timestamp: i64) ![]const u8 {     const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = @intCast(timestamp) };     const epoch_day = epoch_seconds.getEpochDay();     const year_day = epoch_day.calculateYearDay();     const month_day = year_day.calculateMonthDay();      var buffer: [32]u8 = undefined;     const formatted = try std.fmt.bufPrint(&amp;buffer, \"{d:0&gt;2}.{d:0&gt;2}.{d}\", .{         month_day.day_index + 1,         @intFromEnum(month_day.month) + 1,         year_day.year,     });      \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043a\u043e\u043f\u0438\u044e \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 \u043a\u0443\u0447\u0435     return allocator.dupe(u8, formatted); }  \/\/\/ \u041f\u043e\u0438\u0441\u043a \u0433\u0440\u0443\u043f\u043f\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \/\/\/ \u041d\u0435 \u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043d\u043e\u0432\u0443\u044e \u043f\u0430\u043c\u044f\u0442\u044c, \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0449\u0435\u0442 \u043f\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0434\u0430\u043d\u043d\u044b\u043c fn findGroupForUser(login: []const u8, groups: *std.StringArrayHashMap(std.ArrayList([]const u8))) ?[]const u8 {     var it = groups.iterator();     while (it.next()) |entry| {         for (entry.value_ptr.items) |user| {             if (std.mem.eql(u8, user, login)) {                 return entry.key_ptr.*; \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443             }         }     }     return null; }  \/\/\/ \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 \/\/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u0442\u0440\u043e\u043a\u0438 fn getGroupsWithUsers(allocator: std.mem.Allocator, pool: *pg.Pool) !std.StringArrayHashMap(std.ArrayList([]const u8)) {     var groups = std.StringArrayHashMap(std.ArrayList([]const u8)).init(allocator);      var result = try pool.query(         \\\\SELECT g.name AS group_name, u.login AS user_login         \\\\FROM groups g         \\\\JOIN group_users gu ON g.id = gu.group_id         \\\\JOIN users u ON gu.user_id = u.id         \\\\WHERE g.message_status = true         \\\\ORDER BY g.name, u.login     , .{});     defer result.deinit(); \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435      while (try result.next()) |row| {         const group_name = row.get([]const u8, 0);         const user_login = row.get([]const u8, 1);          \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u043a\u043e\u043f\u0438\u0438 \u0441\u0442\u0440\u043e\u043a         const owned_group_name = try allocator.dupe(u8, group_name);         const owned_user_login = try allocator.dupe(u8, user_login);          if (groups.getPtr(owned_group_name)) |users_list| {             \/\/ \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0433\u0440\u0443\u043f\u043f\u0443             try users_list.append(owned_user_login);         } else {             \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u043e\u0432\u0443\u044e \u0433\u0440\u0443\u043f\u043f\u0443 \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c             var users = std.ArrayList([]const u8).init(allocator);             try users.append(owned_user_login);             try groups.put(owned_group_name, users);         }     }      return groups; }  \/\/\/ \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f \/\/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u0442\u0440\u043e\u043a\u0438 fn getGroupMessages(allocator: std.mem.Allocator, pool: *pg.Pool) !std.StringArrayHashMap([]const u8) {     var messages = std.StringArrayHashMap([]const u8).init(allocator);      var result = try pool.query(         \\\\SELECT g.name AS group_name, gm.message_text AS message_text         \\\\FROM group_messages gm         \\\\JOIN groups g ON gm.group_id = g.id         \\\\WHERE g.message_status = true         \\\\ORDER BY g.name     , .{});     defer result.deinit();      while (try result.next()) |row| {         const group_name = row.get([]const u8, 0);         const message_text = row.get([]const u8, 1);          \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u043a\u043e\u043f\u0438\u0438 \u0441\u0442\u0440\u043e\u043a         const owned_group_name = try allocator.dupe(u8, group_name);         const owned_message = try allocator.dupe(u8, message_text);          try messages.put(owned_group_name, owned_message);     }      return messages; }  \/\/\/ \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \/\/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u0441\u0442\u0440\u043e\u043a\u0438 fn getIndividualMessages(allocator: std.mem.Allocator, pool: *pg.Pool) !std.StringArrayHashMap([]const u8) {     var messages = std.StringArrayHashMap([]const u8).init(allocator);      var result = try pool.query(         \\\\SELECT u.login AS user_login, um.message_text AS message_text         \\\\FROM user_messages um         \\\\JOIN users u ON um.user_id = u.id         \\\\WHERE u.accept_messages = true         \\\\ORDER BY u.login     , .{});     defer result.deinit();      while (try result.next()) |row| {         const user_login = row.get([]const u8, 0);         const message_text = row.get([]const u8, 1);          \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u043a\u043e\u043f\u0438\u0438 \u0441\u0442\u0440\u043e\u043a         const owned_login = try allocator.dupe(u8, user_login);         const owned_message = try allocator.dupe(u8, message_text);          try messages.put(owned_login, owned_message);     }      return messages; }  pub fn main() !void {     \/\/ \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0430\u043b\u043b\u043e\u043a\u0430\u0442\u043e\u0440\u0430 (\u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435)     var gpa = std.heap.GeneralPurposeAllocator(.{}){};     defer _ = gpa.deinit(); \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0443\u0442\u0435\u0447\u0435\u043a \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435     const allocator = gpa.allocator();      \/\/ \u0427\u0442\u0435\u043d\u0438\u0435 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u0438 (\u043d\u0443\u0436\u043d\u043e \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0442\u044c)     const args = try std.process.argsAlloc(allocator);     defer std.process.argsFree(allocator, args);      if (args.len &lt; 2) {         std.log.err(\"Usage: {s} &lt;login&gt; [config_path]\", .{args[0]});         return error.MissingArguments;     }     const login = args[1];     const config_path = if (args.len &gt; 2) args[2] else \"config.toml\";      \/\/ \u0427\u0442\u0435\u043d\u0438\u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0430 (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0444\u0430\u0439\u043b\u0430)     const config_file = try std.fs.cwd().readFileAlloc(allocator, config_path, 1 &lt;&lt; 20);     defer allocator.free(config_file); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u043f\u043e\u0441\u043b\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f      \/\/ \u041f\u0430\u0440\u0441\u0438\u043d\u0433 \u043a\u043e\u043d\u0444\u0438\u0433\u0430 (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442 \u043f\u0430\u043c\u044f\u0442\u044c \u043f\u043e\u0434 \u0441\u0442\u0440\u043e\u043a\u0438)     const config = try parseToml(allocator, config_file);     defer {         \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u043a\u043e\u043d\u0444\u0438\u0433\u0430         allocator.free(config.host);         allocator.free(config.username);         allocator.free(config.password);         allocator.free(config.database);     }      \/\/ \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043f\u0443\u043b\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 (\u043d\u0443\u0436\u043d\u043e \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0442\u044c)     var pool = try pg.Pool.init(allocator, .{         .size = config.pool_size,         .connect = .{             .port = config.port,             .host = config.host,         },         .auth = .{             .username = config.username,             .database = config.database,             .password = config.password,             .timeout = config.timeout_ms,         },     });     defer pool.deinit(); \/\/ \u0417\u0430\u043a\u0440\u044b\u0432\u0430\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435      \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f     var user_result = try pool.query(         \\\\SELECT active, to_rm, proxy_server_id          \\\\FROM users          \\\\WHERE login = $1         \\\\LIMIT 1     , .{login});     defer user_result.deinit(); \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435      if (try user_result.next()) |row| {         const active = row.get(bool, 0);         const to_rm = row.get(bool, 1);         const proxy_server_id = row.get(?i32, 2);          std.log.info(\"User found: active={}, to_rm={}\", .{ active, to_rm });          \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u0440\u043e\u043a\u0441\u0438-\u0441\u0435\u0440\u0432\u0435\u0440\u0430         if (proxy_server_id) |proxy_id| {             std.log.info(\"User has proxy server with id={}\", .{proxy_id});              var proxy_result = try pool.query(                 \\\\SELECT host(ip) as ip_text                  \\\\FROM proxy_servers                  \\\\WHERE id = $1 and strong = true                 \\\\LIMIT 1             , .{proxy_id});             defer proxy_result.deinit();              if (try proxy_result.next()) |proxy_row| {                 const ip = proxy_row.get([]const u8, 0);                 std.log.info(\"Proxy server IP: {s}\", .{ip});             } else {                 std.log.warn(\"Proxy server with id={} not found\", .{proxy_id});             }         } else {             std.log.info(\"User has no proxy server\", .{});         }          \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 (\u043d\u0443\u0436\u043d\u043e \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0442\u044c!)         if (try getBannerMessage(allocator, pool, login)) |banner| {             defer allocator.free(banner.text); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0442\u0435\u043a\u0441\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f             std.log.info(\"Banner message (priority: {}): {s}\", .{ banner.priority, banner.text });         } else {             std.log.info(\"No banner message for user\", .{});         }     } else {         std.log.err(\"User with login '{s}' not found\", .{login});     } } <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443 \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 2 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430: \u043b\u043e\u0433\u0438\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0443\u0442\u044c \u043a \u0444\u0430\u0439\u043b\u0443 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438, \u0432\u044b\u0432\u043e\u0434\u0438\u0442 \u0435\u0433\u043e \u0431\u0430\u043d\u043d\u0435\u0440.<\/p>\n<p>\u0422\u0430\u043a \u0436\u0435 \u043c\u044b \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043f\u0440\u043e\u043a\u0441\u0438-\u0441\u0435\u0440\u0432\u0435\u0440 \u0441 \u0440\u0435\u0436\u0438\u043c\u043e\u043c &#171;strong&#187; (\u0435\u0441\u043b\u0438 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d, \u0442\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441 \u0434\u0440\u0443\u0433\u0438\u0445 \u0430\u0434\u0440\u0435\u0441\u043e\u0432 \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043d\u0435\u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u044b).<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0441 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u043c\u0438:<\/p>\n<details class=\"spoiler\">\n<summary>banner_check.zig<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">\/\/ \u0418\u043c\u043f\u043e\u0440\u0442 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0438 \u043c\u043e\u0434\u0443\u043b\u044f PostgreSQL const std = @import(\"std\"); const pg = @import(\"pg\");  \/\/ \u041a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a PostgreSQL const PgConfig = struct {     host: []const u8 = \"localhost\",  \/\/ \u0425\u043e\u0441\u0442 \u0411\u0414 (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438)     port: u16 = 5432,               \/\/ \u041f\u043e\u0440\u0442 \u0411\u0414     username: []const u8 = \"postgres\", \/\/ \u041b\u043e\u0433\u0438\u043d (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438)     password: []const u8 = \"postgres\", \/\/ \u041f\u0430\u0440\u043e\u043b\u044c (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438)     database: []const u8 = \"ocserv\",   \/\/ \u0418\u043c\u044f \u0411\u0414 (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438) };  \/\/ \u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442\u044b \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 pub const MessagePriority = enum {     global,      \/\/ \u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 (\u0432\u044b\u0441\u0448\u0438\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442)     group,       \/\/ \u0413\u0440\u0443\u043f\u043f\u043e\u0432\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435     individual,  \/\/ \u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435     expire,      \/\/ \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e\u0431 \u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u0438 \u0441\u0440\u043e\u043a\u0430 (\u043d\u0438\u0437\u0448\u0438\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442) };  \/\/ \u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f pub const BannerMessage = struct {     text: []const u8,         \/\/ \u0422\u0435\u043a\u0441\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438)     priority: MessagePriority, \/\/ \u041f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f };  \/\/ \u0414\u0430\u043d\u043d\u044b\u0435 \u043e \u0441\u0440\u043e\u043a\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f pub const ExpireData = struct {     start_timestamp: i64,     \/\/ \u0412\u0440\u0435\u043c\u044f \u043d\u0430\u0447\u0430\u043b\u0430 (timestamp)     end_timestamp: i64,       \/\/ \u0412\u0440\u0435\u043c\u044f \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f (timestamp)     start_date_str: []const u8, \/\/ \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0434\u0430\u0442\u0430 \u043d\u0430\u0447\u0430\u043b\u0430 (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438)     end_date_str: []const u8,  \/\/ \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0434\u0430\u0442\u0430 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f (\u0432\u044b\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438) };  \/\/ C-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u0440\u043e\u043a\u0441\u0438-\u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 true, \u0435\u0441\u043b\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043e, false - \u0435\u0441\u043b\u0438 \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u043e pub export fn check_user_proxy(     username: [*:0]const u8, \/\/ \u0412\u0445\u043e\u0434\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440: \u043b\u043e\u0433\u0438\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (null-terminated \u0441\u0442\u0440\u043e\u043a\u0430)     remote_addr: [*:0]const u8, \/\/ \u0412\u0445\u043e\u0434\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440: IP-\u0430\u0434\u0440\u0435\u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 (null-terminated \u0441\u0442\u0440\u043e\u043a\u0430)     proxy_ip_out: [*:0]u8,    \/\/ \u0412\u044b\u0445\u043e\u0434\u043d\u043e\u0439 \u0431\u0443\u0444\u0435\u0440 \u0434\u043b\u044f IP \u043f\u0440\u043e\u043a\u0441\u0438-\u0441\u0435\u0440\u0432\u0435\u0440\u0430 (\u043c\u0438\u043d\u0438\u043c\u0443\u043c 256 \u0431\u0430\u0439\u0442) ) callconv(.C) bool {     \/\/ \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c GeneralPurposeAllocator \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u043c\u044f\u0442\u044c\u044e     var gpa = std.heap.GeneralPurposeAllocator(.{}){};     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0443\u0442\u0435\u0447\u0435\u043a \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435 \u0438\u0437 \u0444\u0443\u043d\u043a\u0446\u0438\u0438     defer _ = gpa.deinit();     const allocator = gpa.allocator();      \/\/ \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0439 \u0431\u0443\u0444\u0435\u0440 \u043d\u0443\u043b\u044f\u043c\u0438     @memset(proxy_ip_out[0..256], 0);      \/\/ \u041b\u043e\u0433\u0438\u0440\u0443\u0435\u043c \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b     std.log.debug(\"Checking proxy for user: {s}, IP: {s}\", .{ username, remote_addr });      \/\/ 1. \u0427\u0438\u0442\u0430\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b     const config_file = std.fs.cwd().readFileAlloc(allocator, \"\/etc\/ocserv\/banner.toml\", 1 &lt;&lt; 20) catch |err| {         std.log.err(\"Failed to read config: {}\", .{err});         return true; \/\/ \u041f\u0440\u0438 \u043e\u0448\u0438\u0431\u043a\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435     };     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438 \u0444\u0430\u0439\u043b\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438     defer allocator.free(config_file);      \/\/ \u041f\u0430\u0440\u0441\u0438\u043c TOML \u043a\u043e\u043d\u0444\u0438\u0433     const pg_config = parse_toml(allocator, config_file) catch |err| {         std.log.err(\"Invalid TOML config: {}\", .{err});         return true; \/\/ \u041f\u0440\u0438 \u043e\u0448\u0438\u0431\u043a\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435     };     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438     defer {         allocator.free(pg_config.host);         allocator.free(pg_config.username);         allocator.free(pg_config.password);         allocator.free(pg_config.database);     }      \/\/ 2. \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0443\u043b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u0441 PostgreSQL     var pool = pg.Pool.init(allocator, .{         .size = 3, \/\/ \u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u0432 \u043f\u0443\u043b\u0435         .connect = .{             .port = pg_config.port,             .host = pg_config.host,         },         .auth = .{             .username = pg_config.username,             .database = pg_config.database,             .password = pg_config.password,             .timeout = 5000, \/\/ \u0422\u0430\u0439\u043c\u0430\u0443\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f 5 \u0441\u0435\u043a\u0443\u043d\u0434         },     }) catch |err| {         std.log.err(\"DB connection failed: {}\", .{err});         return true; \/\/ \u041f\u0440\u0438 \u043e\u0448\u0438\u0431\u043a\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435     };     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u0435 \u043f\u0443\u043b\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435     defer pool.deinit();      \/\/ 3. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u043d\u0430\u043b\u0438\u0447\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0431\u0430\u0437\u0435     var user_result = pool.query(         \\\\SELECT proxy_server_id FROM users WHERE login = $1 LIMIT 1     , .{std.mem.span(username)}) catch |err| {         std.log.err(\"User query failed: {}\", .{err});         return true;     };     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430     defer user_result.deinit();      \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430     const user_row = user_result.next() catch |err| {         std.log.err(\"User query iteration failed: {}\", .{err});         return true;     } orelse {         std.log.err(\"User not found: {s}\", .{std.mem.span(username)});         return true;     };      \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c ID \u043f\u0440\u043e\u043a\u0441\u0438-\u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (\u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c null)     const proxy_id: ?i32 = user_row.get(?i32, 0);     if (proxy_id) |id| {         std.log.debug(\"User proxy_id: {}\", .{id});     } else {         std.log.debug(\"User has no proxy_id\", .{});     }      \/\/ 4. \u0415\u0441\u043b\u0438 \u043f\u0440\u043e\u043a\u0441\u0438 \u043d\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d - \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435     if (proxy_id == null) return true;      \/\/ 5. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c IP \u043f\u0440\u043e\u043a\u0441\u0438-\u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0438\u0437 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445     var proxy_ip_result = pool.query(         \\\\SELECT host(ip) FROM proxy_servers WHERE id = $1 AND strong = true LIMIT 1     , .{proxy_id}) catch |err| {         std.log.err(\"Proxy query failed: {}\", .{err});         return true;     };     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430     defer proxy_ip_result.deinit();      \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430     const proxy_ip_row = proxy_ip_result.next() catch |err| {         std.log.err(\"Proxy IP query iteration failed: {}\", .{err});         return true;     } orelse {         std.log.err(\"Proxy not found or not strong: id={}\", .{proxy_id.?});         return true;     };      \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c IP-\u0430\u0434\u0440\u0435\u0441 \u043f\u0440\u043e\u043a\u0441\u0438     const proxy_ip = proxy_ip_row.get([]const u8, 0);     if (proxy_ip.len == 0) {         std.log.err(\"Empty proxy IP for proxy_id={}\", .{proxy_id.?});         return true;     }      std.log.debug(\"Found proxy IP: {s}\", .{proxy_ip});      \/\/ \u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c proxy IP \u0432 \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0439 \u0431\u0443\u0444\u0435\u0440 (\u043d\u0435 \u0431\u043e\u043b\u0435\u0435 255 \u0441\u0438\u043c\u0432\u043e\u043b\u043e\u0432 + null-terminator)     const copy_len = @min(proxy_ip.len, 255);     @memcpy(proxy_ip_out[0..copy_len], proxy_ip[0..copy_len]);     proxy_ip_out[copy_len] = 0; \/\/ \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c null-terminator     std.log.debug(\"Copied proxy IP to output: '{s}'\", .{proxy_ip_out[0..copy_len]});      \/\/ 6. \u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c IP-\u0430\u0434\u0440\u0435\u0441\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0438 \u043f\u0440\u043e\u043a\u0441\u0438     const client_ip = std.mem.span(remote_addr);     std.log.debug(\"Comparing client IP: {s} with proxy IP: {s}\", .{ client_ip, proxy_ip });      \/\/ \u041f\u0430\u0440\u0441\u0438\u043c IP-\u0430\u0434\u0440\u0435\u0441\u0430 \u0434\u043b\u044f \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f     const proxy_addr = std.net.Address.parseIp(proxy_ip, 0) catch |err| {         std.log.err(\"Invalid proxy IP '{s}': {}\", .{ proxy_ip, err });         return true;     };     const client_addr = std.net.Address.parseIp(client_ip, 0) catch |err| {         std.log.err(\"Invalid client IP '{s}': {}\", .{ client_ip, err });         return true;     };      \/\/ \u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c \u0431\u0438\u043d\u0430\u0440\u043d\u044b\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0430\u0434\u0440\u0435\u0441\u043e\u0432     const equal = std.mem.eql(u8, std.mem.asBytes(&amp;proxy_addr.in.sa.addr), std.mem.asBytes(&amp;client_addr.in.sa.addr));     std.log.debug(\"IP comparison result: {}\", .{equal});     return equal; }  \/\/ C-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u0430\u043d\u043d\u0435\u0440\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f pub export fn get_banner(     username: [*:0]const u8, \/\/ \u0412\u0445\u043e\u0434\u043d\u043e\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440: \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f     banner_out: [*:0]u8,     \/\/ \u0412\u044b\u0445\u043e\u0434\u043d\u043e\u0439 \u0431\u0443\u0444\u0435\u0440 \u0434\u043b\u044f \u0431\u0430\u043d\u043d\u0435\u0440\u0430     banner_len: usize,       \/\/ \u0420\u0430\u0437\u043c\u0435\u0440 \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0431\u0443\u0444\u0435\u0440\u0430 ) callconv(.C) bool {       \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 true \u0435\u0441\u043b\u0438 \u0431\u0430\u043d\u043d\u0435\u0440 \u043f\u043e\u043b\u0443\u0447\u0435\u043d     \/\/ \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0430\u043b\u043b\u043e\u043a\u0430\u0442\u043e\u0440     var gpa = std.heap.GeneralPurposeAllocator(.{}){};     defer _ = gpa.deinit();     const allocator = gpa.allocator();      \/\/ \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0439 \u0431\u0443\u0444\u0435\u0440 \u043d\u0443\u043b\u044f\u043c\u0438     @memset(banner_out[0..banner_len], 0);      std.log.debug(\"Getting banner for user: {s}\", .{username});      \/\/ 1. \u0427\u0438\u0442\u0430\u0435\u043c \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0439 \u0444\u0430\u0439\u043b     const config_file = std.fs.cwd().readFileAlloc(allocator, \"\/etc\/ocserv\/banner.toml\", 1 &lt;&lt; 20) catch |err| {         std.log.err(\"Failed to read config: {}\", .{err});         return false;     };     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438 \u0444\u0430\u0439\u043b\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438     defer allocator.free(config_file);      \/\/ \u041f\u0430\u0440\u0441\u0438\u043c TOML \u043a\u043e\u043d\u0444\u0438\u0433     const pg_config = parse_toml(allocator, config_file) catch |err| {         std.log.err(\"Invalid TOML config: {}\", .{err});         return false;     };     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u043e\u043a \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438     defer {         allocator.free(pg_config.host);         allocator.free(pg_config.username);         allocator.free(pg_config.password);         allocator.free(pg_config.database);     }      \/\/ 2. \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u0443\u043b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u0441 PostgreSQL     var pool = pg.Pool.init(allocator, .{         .size = 3,         .connect = .{             .port = pg_config.port,             .host = pg_config.host,         },         .auth = .{             .username = pg_config.username,             .database = pg_config.database,             .password = pg_config.password,             .timeout = 5000,         },     }) catch |err| {         std.log.err(\"DB connection failed: {}\", .{err});         return false;     };     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u0435 \u043f\u0443\u043b\u0430 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435     defer pool.deinit();      \/\/ 3. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f     const login = std.mem.span(username);     const maybe_message = getBannerMessage(allocator, pool, login) catch |err| {         std.log.err(\"Failed to get banner message: {}\", .{err});         return false;     };      \/\/ \u0415\u0441\u043b\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e     if (maybe_message) |message| {         \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0442\u0435\u043a\u0441\u0442\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f (\u043a\u0440\u043e\u043c\u0435 expire, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0441\u0442\u0440\u043e\u043a\u0438)         defer {             if (message.priority != .expire) {                 allocator.free(message.text);             }         }          \/\/ \u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0439 \u0431\u0443\u0444\u0435\u0440         const copy_len = @min(message.text.len, banner_len - 1);         @memcpy(banner_out[0..copy_len], message.text[0..copy_len]);         banner_out[copy_len] = 0; \/\/ \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c null-terminator         std.log.debug(\"Copied banner message: '{s}'\", .{banner_out[0..copy_len]});         return true;     }      return false; }  \/\/ \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f fn getBannerMessage(     allocator: std.mem.Allocator, \/\/ \u0410\u043b\u043b\u043e\u043a\u0430\u0442\u043e\u0440 \u0434\u043b\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445     pool: *pg.Pool,              \/\/ \u041f\u0443\u043b \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0439 \u0441 \u0411\u0414     login: []const u8,           \/\/ \u041b\u043e\u0433\u0438\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f ) !?BannerMessage {     \/\/ 1. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 (\u043d\u0430\u0438\u0432\u044b\u0441\u0448\u0438\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442)     {         var global_result = try pool.query(             \\\\SELECT message_text FROM global_message WHERE status = true LIMIT 1         , .{});         defer global_result.deinit(); \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430          if (try global_result.next()) |row| {             const message = row.get([]const u8, 0);             \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043e\u043f\u0438\u044e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430             return BannerMessage{                 .text = try allocator.dupe(u8, message),                 .priority = MessagePriority.global,             };         }     }      \/\/ 2. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0433\u0440\u0443\u043f\u043f\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0433\u0440\u0443\u043f\u043f     var groups = try getGroupsWithUsers(allocator, pool);     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438 \u0433\u0440\u0443\u043f\u043f     defer {         var it = groups.iterator();         while (it.next()) |entry| {             \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0438\u043c\u0435\u043d\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0432 \u0433\u0440\u0443\u043f\u043f\u0435             for (entry.value_ptr.items) |user| {                 allocator.free(user);             }             entry.value_ptr.deinit(); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439             allocator.free(entry.key_ptr.*); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0438\u043c\u044f \u0433\u0440\u0443\u043f\u043f\u044b         }         groups.deinit(); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0445\u044d\u0448-\u043c\u0430\u043f \u0433\u0440\u0443\u043f\u043f     }      \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0433\u0440\u0443\u043f\u043f     var group_messages = try getGroupMessages(allocator, pool);     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0433\u0440\u0443\u043f\u043f     defer {         var it = group_messages.iterator();         while (it.next()) |entry| {             allocator.free(entry.key_ptr.*); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0438\u043c\u044f \u0433\u0440\u0443\u043f\u043f\u044b             allocator.free(entry.value_ptr.*); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0442\u0435\u043a\u0441\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f         }         group_messages.deinit();     }      \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f\u044b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f     if (findGroupForUser(login, &amp;groups)) |group_name| {         if (group_messages.get(group_name)) |message| {             \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043e\u043f\u0438\u044e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430             return BannerMessage{                 .text = try allocator.dupe(u8, message),                 .priority = MessagePriority.group,             };         }     }      \/\/ 3. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f     var individual_messages = try getIndividualMessages(allocator, pool);     \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438 \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439     defer {         var it = individual_messages.iterator();         while (it.next()) |entry| {             allocator.free(entry.key_ptr.*); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u043b\u043e\u0433\u0438\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f             allocator.free(entry.value_ptr.*); \/\/ \u041e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0430\u0435\u043c \u0442\u0435\u043a\u0441\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f         }         individual_messages.deinit();     }      \/\/ \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f     if (individual_messages.get(login)) |message| {         \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043e\u043f\u0438\u044e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0430         return BannerMessage{             .text = try allocator.dupe(u8, message),             .priority = MessagePriority.individual,         };     }      \/\/ 4. \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0441\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043a\u043b\u044e\u0447\u0430 (\u043d\u0438\u0437\u0448\u0438\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442)     {         var user_result = try pool.query(             \\\\SELECT expire FROM users WHERE login = $1 LIMIT 1         , .{login});         defer user_result.deinit(); \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430          if (try user_result.next()) |row| {             const expire_json = row.get(?[]const u8, 0);             if (expire_json) |json| {                 \/\/ \u041f\u0430\u0440\u0441\u0438\u043c JSON \u0441 \u0434\u0430\u0442\u0430\u043c\u0438                 const expire = try parseExpireJson(allocator, json);                 \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0434\u0430\u0442                 defer {                     allocator.free(expire.start_date_str);                     allocator.free(expire.end_date_str);                 }                  \/\/ \u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u043e\u0441\u0442\u0430\u0432\u0448\u0435\u0435\u0441\u044f \u0432\u0440\u0435\u043c\u044f                 const now = std.time.timestamp();                 const seconds_left = expire.end_timestamp - now;                 const days_left = @divTrunc(seconds_left, 86400);                 const hours_left = @divTrunc(@mod(seconds_left, 86400), 3600);                  \/\/ \u0424\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043e\u0441\u0442\u0430\u0432\u0448\u0435\u0433\u043e\u0441\u044f \u0432\u0440\u0435\u043c\u0435\u043d\u0438                 if (days_left &gt;= 30 and days_left &lt; 31) {                     \/\/ \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e \u043d\u043e\u0432\u043e\u043c \u0442\u0430\u0440\u0438\u0444\u0435 (\u0440\u0443\u0441\u0441\u043a\u0438\u0439 \u0438 \u0442\u0443\u0440\u043a\u043c\u0435\u043d\u0441\u043a\u0438\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u044b)                     const msg1 = try std.fmt.allocPrint(allocator,                          \"\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u0435\u043c! \u0412\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438 \u043b\u0443\u0447\u0448\u0435\u0435. \u0412\u0430\u0448 \u043d\u043e\u0432\u044b\u0439 \u0442\u0430\u0440\u0438\u0444 \u0443\u0436\u0435 \u0430\u043a\u0442\u0438\u0432\u0435\u043d \u0441 {s} \u0433\u043e\u0434\u0430 \u0434\u043e {s} \u0433\u043e\u0434\u0430. \u041d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u0439\u0442\u0435\u0441\u044c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u043e\u043c!\",                          .{ expire.start_date_str, expire.end_date_str });                     defer allocator.free(msg1);                      const msg2 = try std.fmt.allocPrint(allocator,                          \"Sizi gutla\u00fdarys! I\u0148 gowy tarifi sa\u00fdlady\u0148yz. T\u00e4ze tarifi\u0148iz {s} -- {s} senesi i\u015fje\u0148dir. Howpsuz we \u00e7alt internetden lezzet aly\u0148!\",                          .{ expire.start_date_str, expire.end_date_str });                     defer allocator.free(msg2);                      \/\/ \u0421\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440 \u044f\u0437\u044b\u043a\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f                     const chosen = if (std.crypto.random.int(u1) == 0) msg1 else msg2;                     return BannerMessage{                         .text = try allocator.dupe(u8, chosen),                         .priority = MessagePriority.expire,                     };                 }                  \/\/ ... (\u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438 \u0434\u043b\u044f \u0434\u0440\u0443\u0433\u0438\u0445 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0438\u043d\u0442\u0435\u0440\u0432\u0430\u043b\u043e\u0432)                 else if (days_left &gt;= -3 and days_left &lt; 0) {                     \/\/ \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043e \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u043c \u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0435                     const msg1: []const u8 = \"\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u0430\u0448 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d \u0438\u0437-\u0437\u0430 \u043d\u0443\u043b\u0435\u0432\u043e\u0433\u043e \u0431\u0430\u043b\u0430\u043d\u0441\u0430.\";                     const msg2: []const u8 = \"Bagy\u015fla\u0148, h\u00e4zirki wagtda balansy\u0148yz nol bolany \u00fc\u00e7in hasaby\u0148yz bloklanyldy.\";                      const chosen = if (std.crypto.random.int(u1) == 0) msg1 else msg2;                     return BannerMessage{                         .text = try allocator.dupe(u8, chosen),                         .priority = MessagePriority.expire,                     };                 }             }         }     }      return null; \/\/ \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u043e }  \/\/ \u0412\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438  \/\/ \u041f\u0430\u0440\u0441\u0438\u043d\u0433 JSON \u0441 \u0434\u0430\u0442\u0430\u043c\u0438 \u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u044f \u0441\u0440\u043e\u043a\u0430 fn parseExpireJson(allocator: std.mem.Allocator, json_str: []const u8) !ExpireData {     const parsed = try std.json.parseFromSlice(struct {         start: i64,         end: i64,     }, allocator, json_str, .{});     defer parsed.deinit(); \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043f\u0430\u0440\u0441\u0435\u0440\u0430      const start_timestamp = parsed.value.start;     const end_timestamp = parsed.value.end;      \/\/ \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u043c \u0434\u0430\u0442\u044b \u0432 \u0441\u0442\u0440\u043e\u043a\u0438     const start_date_str = try formatTimestamp(allocator, start_timestamp);     const end_date_str = try formatTimestamp(allocator, end_timestamp);      return ExpireData{         .start_timestamp = start_timestamp,         .end_timestamp = end_timestamp,         .start_date_str = start_date_str,         .end_date_str = end_date_str,     }; }  \/\/ \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 \u043c\u0435\u0442\u043a\u0438 \u0432 \u0441\u0442\u0440\u043e\u043a\u0443 (DD.MM.YYYY) fn formatTimestamp(allocator: std.mem.Allocator, timestamp: i64) ![]const u8 {     \/\/ \u041a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u0435\u043c timestamp \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0434\u0430\u0442\u044b     const epoch_seconds = std.time.epoch.EpochSeconds{ .secs = @intCast(timestamp) };     const epoch_day = epoch_seconds.getEpochDay();     const year_day = epoch_day.calculateYearDay();     const month_day = year_day.calculateMonthDay();      \/\/ \u0424\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u0443\u0435\u043c \u0432 \u0431\u0443\u0444\u0435\u0440     var buffer: [32]u8 = undefined;     const formatted = try std.fmt.bufPrint(&amp;buffer, \"{d:0&gt;2}.{d:0&gt;2}.{d}\", .{         month_day.day_index + 1, \/\/ \u0414\u0435\u043d\u044c (1-based)         @intFromEnum(month_day.month) + 1, \/\/ \u041c\u0435\u0441\u044f\u0446 (1-based)         year_day.year, \/\/ \u0413\u043e\u0434     });      \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043a\u043e\u043f\u0438\u044e \u0441\u0442\u0440\u043e\u043a\u0438     return allocator.dupe(u8, formatted); }  \/\/ \u041f\u043e\u0438\u0441\u043a \u0433\u0440\u0443\u043f\u043f\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f fn findGroupForUser(login: []const u8, groups: *std.StringArrayHashMap(std.ArrayList([]const u8))) ?[]const u8 {     var it = groups.iterator();     while (it.next()) |entry| {         for (entry.value_ptr.items) |user| {             if (std.mem.eql(u8, user, login)) {                 return entry.key_ptr.*; \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0438\u043c\u044f \u0433\u0440\u0443\u043f\u043f\u044b             }         }     }     return null; \/\/ \u0413\u0440\u0443\u043f\u043f\u0430 \u043d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 }  \/\/ \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430 \u0433\u0440\u0443\u043f\u043f \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438 fn getGroupsWithUsers(allocator: std.mem.Allocator, pool: *pg.Pool) !std.StringArrayHashMap(std.ArrayList([]const u8)) {     var groups = std.StringArrayHashMap(std.ArrayList([]const u8)).init(allocator);      var result = try pool.query(         \\\\SELECT g.name AS group_name, u.login AS user_login         \\\\FROM groups g         \\\\JOIN group_users gu ON g.id = gu.group_id         \\\\JOIN users u ON gu.user_id = u.id         \\\\WHERE g.message_status = true         \\\\ORDER BY g.name, u.login     , .{});     defer result.deinit(); \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430      \/\/ \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430     while (try result.next()) |row| {         const group_name = row.get([]const u8, 0);         const user_login = row.get([]const u8, 1);          \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043e\u043f\u0438\u0438 \u0441\u0442\u0440\u043e\u043a \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432 \u0445\u044d\u0448-\u043c\u0430\u043f\u0435         const owned_group_name = try allocator.dupe(u8, group_name);         const owned_user_login = try allocator.dupe(u8, user_login);          \/\/ \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u0433\u0440\u0443\u043f\u043f\u0443         if (groups.getPtr(owned_group_name)) |users_list| {             try users_list.append(owned_user_login);         } else {             var users = std.ArrayList([]const u8).init(allocator);             try users.append(owned_user_login);             try groups.put(owned_group_name, users);         }     }      return groups; }  \/\/ \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0433\u0440\u0443\u043f\u043f fn getGroupMessages(allocator: std.mem.Allocator, pool: *pg.Pool) !std.StringArrayHashMap([]const u8) {     var messages = std.StringArrayHashMap([]const u8).init(allocator);      var result = try pool.query(         \\\\SELECT g.name AS group_name, gm.message_text AS message_text         \\\\FROM group_messages gm         \\\\JOIN groups g ON gm.group_id = g.id         \\\\WHERE g.message_status = true         \\\\ORDER BY g.name     , .{});     defer result.deinit(); \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430      \/\/ \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430     while (try result.next()) |row| {         const group_name = row.get([]const u8, 0);         const message_text = row.get([]const u8, 1);          \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043e\u043f\u0438\u0438 \u0441\u0442\u0440\u043e\u043a \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432 \u0445\u044d\u0448-\u043c\u0430\u043f\u0435         const owned_group_name = try allocator.dupe(u8, group_name);         const owned_message = try allocator.dupe(u8, message_text);          try messages.put(owned_group_name, owned_message);     }      return messages; }  \/\/ \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 fn getIndividualMessages(allocator: std.mem.Allocator, pool: *pg.Pool) !std.StringArrayHashMap([]const u8) {     var messages = std.StringArrayHashMap([]const u8).init(allocator);      var result = try pool.query(         \\\\SELECT u.login AS user_login, um.message_text AS message_text         \\\\FROM user_messages um         \\\\JOIN users u ON um.user_id = u.id         \\\\WHERE u.accept_messages = true         \\\\ORDER BY u.login     , .{});     defer result.deinit(); \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c \u043e\u0441\u0432\u043e\u0431\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430      \/\/ \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430     while (try result.next()) |row| {         const user_login = row.get([]const u8, 0);         const message_text = row.get([]const u8, 1);          \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u043e\u043f\u0438\u0438 \u0441\u0442\u0440\u043e\u043a \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432 \u0445\u044d\u0448-\u043c\u0430\u043f\u0435         const owned_login = try allocator.dupe(u8, user_login);         const owned_message = try allocator.dupe(u8, message_text);          try messages.put(owned_login, owned_message);     }      return messages; }  \/\/ \u041f\u0430\u0440\u0441\u0438\u043d\u0433 TOML \u043a\u043e\u043d\u0444\u0438\u0433\u0430 (\u0443\u043f\u0440\u043e\u0449\u0435\u043d\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f) fn parse_toml(allocator: std.mem.Allocator, data: []const u8) !PgConfig {     var config = PgConfig{};     var lines = std.mem.tokenizeSequence(u8, data, \"\\n\");      \/\/ \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0444\u0430\u0439\u043b\u0430     while (lines.next()) |line| {         const trimmed = std.mem.trim(u8, line, \" \\t\");         \/\/ \u041f\u0440\u043e\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u0443\u0441\u0442\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438         if (trimmed.len == 0 or trimmed[0] == '#') continue;          \/\/ \u0420\u0430\u0437\u0431\u0438\u0440\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0438 \u0432\u0438\u0434\u0430 \"\u043a\u043b\u044e\u0447 = \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\"         if (std.mem.indexOf(u8, trimmed, \"=\")) |eq_pos| {             const key = std.mem.trim(u8, trimmed[0..eq_pos], \" \\t\\\"'\");             const value = std.mem.trim(u8, trimmed[eq_pos + 1 ..], \" \\t\\\"'\");              \/\/ \u041e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043a\u0430\u0436\u0434\u044b\u0439 \u043a\u043b\u044e\u0447             if (std.mem.eql(u8, key, \"host\")) {                 config.host = try allocator.dupe(u8, value); \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u0434\u043b\u044f \u0445\u043e\u0441\u0442\u0430             } else if (std.mem.eql(u8, key, \"port\")) {                 config.port = try std.fmt.parseInt(u16, value, 10); \/\/ \u041f\u0430\u0440\u0441\u0438\u043c \u043f\u043e\u0440\u0442             } else if (std.mem.eql(u8, key, \"username\") or std.mem.eql(u8, key, \"user\")) {                 config.username = try allocator.dupe(u8, value); \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u0434\u043b\u044f \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f             } else if (std.mem.eql(u8, key, \"password\")) {                 config.password = try allocator.dupe(u8, value); \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u0434\u043b\u044f \u043f\u0430\u0440\u043e\u043b\u044f             } else if (std.mem.eql(u8, key, \"database\")) {                 config.database = try allocator.dupe(u8, value); \/\/ \u0412\u044b\u0434\u0435\u043b\u044f\u0435\u043c \u043f\u0430\u043c\u044f\u0442\u044c \u0434\u043b\u044f \u0438\u043c\u0435\u043d\u0438 \u0411\u0414             }         }     }      return config; }<\/code><\/pre>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <code>pub export fn get_banner( <\/code>\u0432\u043c\u0435\u0441\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e export, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0438\u0437 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b \u0434\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f (\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0441\u043f\u043e\u0439\u043b\u0435\u0440).<\/p>\n<p>\u041d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u044c\u0438 \u044f \u043d\u0430\u0447\u0430\u043b \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u0434 \u0431\u043e\u043b\u0435\u0435 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0435\u0439 \u0431\u0430\u043d\u043d\u0435\u0440\u0430, \u0441 \u043b\u0443\u0447\u0448\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043e\u0439 \u0438 \u043c\u0435\u043d\u044c\u0448\u0438\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0431\u0434, \u0441 \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u0435\u043c \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u043f\u0430\u043c\u044f\u0442\u0438.<\/p>\n<\/div>\n<\/details>\n<p>\u041a\u0430\u043a \u044f \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u043b \u0440\u0430\u0431\u043e\u0442\u0443 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438:<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<p>\u041f\u043e\u043a\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0447\u0443\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0447\u0435\u043c \u0441\u043a\u0440\u043e\u043c\u043d\u044b\u0439, \u044f \u043f\u043e\u043b\u0435\u043d\u0438\u043b\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0435 unit-\u0442\u0435\u0441\u0442\u044b, \u043c\u043e\u043a\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u0434 \u0438 \u0442.\u0434. \u041d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u044d\u0442\u043e\u0442 \u043f\u0443\u043d\u043a\u0442 \u0432 TODO \u043b\u0438\u0441\u0442 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<p>\u041a\u0441\u0442\u0430\u0442\u0438, \u0435\u0441\u043b\u0438 \u0432\u044b \u043e\u0431\u0440\u0430\u0442\u0438\u043b\u0438 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u2014 \u043c\u043e\u0434\u0443\u043b\u044c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 true, \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0435\u0441\u043b\u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439, \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u043d\u043e \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u043d\u043e \u0441 \u0446\u0435\u043b\u044c\u044e \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c UX \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<p>\u0414\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0447\u0442\u043e \u0432\u0441\u0451 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u0437\u0430\u0434\u0443\u043c\u0430\u043d\u043e, \u0431\u044b\u043b\u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u0442\u0430\u043a\u0430\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430, <strong>test\/test_banner.zig:<\/strong><\/p>\n<pre><code class=\"cpp\">const std = @import(\"std\");  \/\/ Live \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 pub fn main() !void {     const allocator = std.heap.page_allocator;      \/\/ \u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u0440\u043e\u043a\u0441\u0438     const ProxyTestCase = struct {         username: []const u8,         remote_ip: []const u8,         expected: bool,         description: []const u8,     };      const proxy_test_cases = [_]ProxyTestCase{         .{             .username = \"test_user\",             .remote_ip = \"192.168.1.1\",             .expected = false,             .description = \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442\",         },         .{             .username = \"axtest\",             .remote_ip = \"1.2.3.4\", \/\/ \u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 IP             .expected = false,             .description = \"\u0421\u0442\u0440\u043e\u0433\u0438\u0439 \u043f\u0440\u043e\u043a\u0441\u0438, IP \u043d\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442\",         },         .{             .username = \"axtest\",             .remote_ip = \"\", \/\/ \u041f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 IP             .expected = true,             .description = \"\u0421\u0442\u0440\u043e\u0433\u0438\u0439 \u043f\u0440\u043e\u043a\u0441\u0438, IP \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442\",         },         .{             .username = \"axtest2\",             .remote_ip = \"5.6.7.8\", \/\/ \u041b\u044e\u0431\u043e\u0439 IP \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c             .expected = true,             .description = \"\u041d\u0435\u0441\u0442\u0440\u043e\u0433\u0438\u0439 \u043f\u0440\u043e\u043a\u0441\u0438\",         },         .{             .username = \"axtest3\",             .remote_ip = \"9.10.11.12\", \/\/ \u041b\u044e\u0431\u043e\u0439 IP \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c             .expected = true,             .description = \"\u041f\u0440\u043e\u043a\u0441\u0438 \u043d\u0435 \u0437\u0430\u0434\u0430\u043d\",         },     };      \/\/ \u0422\u0435\u0441\u0442\u043e\u0432\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0431\u0430\u043d\u043d\u0435\u0440\u043e\u0432     const BannerTestCase = struct {         username: []const u8,         description: []const u8,     };      const banner_test_cases = [_]BannerTestCase{         .{             .username = \"axtest\",             .description = \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c\",         },         .{             .username = \"axtest2\",             .description = \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441 \u0433\u0440\u0443\u043f\u043f\u043e\u0432\u044b\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c\",         },         .{             .username = \"axtest3\",             .description = \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441 \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c\",         },         .{             .username = \"axtest4\",             .description = \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441 \u0438\u0441\u0442\u0435\u043a\u0430\u044e\u0449\u0438\u043c \u0441\u0440\u043e\u043a\u043e\u043c\",         },         .{             .username = \"axtest5\",             .description = \"\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u0435\u0437 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439\",         },     };      std.debug.print(\"\\n=== \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u0440\u043e\u043a\u0441\u0438 ===\\n\", .{});     for (proxy_test_cases) |case| {         \/\/ \u0411\u0443\u0444\u0435\u0440 \u0434\u043b\u044f IP \u043f\u0440\u043e\u043a\u0441\u0438         var proxy_ip: [256]u8 = undefined;         @memset(&amp;proxy_ip, 0);         proxy_ip[proxy_ip.len - 1] = 0; \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c null-terminator          \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u0443\u043b\u044c-\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438         const username_nt = try std.fmt.allocPrintZ(allocator, \"{s}\", .{case.username});         defer allocator.free(username_nt);         const remote_ip_nt = try std.fmt.allocPrintZ(allocator, \"{s}\", .{case.remote_ip});         defer allocator.free(remote_ip_nt);          \/\/ \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u0440\u043e\u043a\u0441\u0438         const res = @import(\"banner_check\").check_user_proxy(             username_nt.ptr,             remote_ip_nt.ptr,             @ptrCast(&amp;proxy_ip),         );          \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0443 IP \u043f\u0440\u043e\u043a\u0441\u0438         const proxy_ip_str = if (proxy_ip[0] != 0)             std.mem.sliceTo(&amp;proxy_ip, 0)         else             \"none\";          \/\/ \u041f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442         const status = if (res == case.expected) \"PASS\" else \"FAIL\";          std.debug.print(\"[{s}] {s}: {s}@{s} =&gt; {} (proxy: {s}, expected {})\\n\", .{             status,             case.description,             case.username,             case.remote_ip,             res,             proxy_ip_str,             case.expected,         });     }      std.debug.print(\"\\n=== \u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 ===\\n\", .{});     for (banner_test_cases) |case| {         \/\/ \u0411\u0443\u0444\u0435\u0440 \u0434\u043b\u044f \u0431\u0430\u043d\u043d\u0435\u0440\u0430 (4KB \u0434\u043e\u043b\u0436\u043d\u043e \u0445\u0432\u0430\u0442\u0438\u0442\u044c \u0434\u043b\u044f \u043b\u044e\u0431\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f)         var banner_buffer: [4096]u8 = undefined;         @memset(&amp;banner_buffer, 0);         banner_buffer[banner_buffer.len - 1] = 0; \/\/ \u0413\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u0443\u0435\u043c null-terminator          \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043d\u0443\u043b\u044c-\u0442\u0435\u0440\u043c\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u043e\u043a\u0443 \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f         const username_nt = try std.fmt.allocPrintZ(allocator, \"{s}\", .{case.username});         defer allocator.free(username_nt);          \/\/ \u0412\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0431\u0430\u043d\u043d\u0435\u0440\u0430         const has_banner = @import(\"banner_check\").get_banner(             username_nt.ptr,             @ptrCast(&amp;banner_buffer),             banner_buffer.len,         );          \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u0442\u0440\u043e\u043a\u0443 \u0431\u0430\u043d\u043d\u0435\u0440\u0430         const banner_str = if (has_banner)             std.mem.sliceTo(&amp;banner_buffer, 0)         else             \"no banner\";          std.debug.print(\"{s}: {s} =&gt; {s}\\n\", .{             case.description,             case.username,             banner_str,         });     } } <\/code><\/pre>\n<\/div>\n<\/details>\n<p>Build.zig:<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">const std = @import(\"std\");  pub fn build(b: *std.Build) void {     \/\/ \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0435 \u043e\u043f\u0446\u0438\u0438     const target = b.standardTargetOptions(.{});     const optimize = b.standardOptimizeOption(.{});      \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044c pg     const pg_module = b.dependency(\"pg\", .{         .target = target,         .optimize = optimize,     }).module(\"pg\");      \/\/ 1. \u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u043d\u044f\u0435\u043c\u044b\u0439 \u0444\u0430\u0439\u043b     const exe = b.addExecutable(.{         .name = \"login-test\",         .root_source_file = b.path(\"src\/main.zig\"),         .target = target,         .optimize = optimize,     });     exe.root_module.addImport(\"pg\", pg_module);     b.installArtifact(exe);      \/\/ 2. \u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443     const lib = b.addStaticLibrary(.{         .name = \"banner_check\",         .root_source_file = b.path(\"src\/banner_check.zig\"),         .target = target,         .optimize = .ReleaseSafe,     });      lib.root_module.addImport(\"pg\", pg_module);     lib.linkLibC();     lib.linkSystemLibrary(\"pq\");     lib.bundle_compiler_rt = true;     b.installArtifact(lib);      \/\/ 3. \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0442\u0435\u0441\u0442\u043e\u0432\u0443\u044e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0443     const test_exe = b.addExecutable(.{         .name = \"test_banner\",         .root_source_file = b.path(\"test\/test_banner.zig\"),         .target = target,         .optimize = optimize,     });      \/\/ \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438     test_exe.root_module.addImport(\"banner_check\", lib.root_module);      test_exe.linkLibrary(lib); \/\/ \u041b\u0438\u043d\u043a\u0443\u0435\u043c \u043d\u0430\u0448\u0443 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443     test_exe.linkLibC();      b.installArtifact(test_exe);      \/\/ 4. \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0448\u0430\u0433 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0442\u0435\u0441\u0442\u043e\u0432     const run_test = b.addRunArtifact(test_exe);     const test_step = b.step(\"test\", \"Run banner check tests\");     test_step.dependOn(&amp;run_test.step);      \/\/ 5. \u041f\u0440\u043e\u0441\u0442\u0430\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u0447\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430     const gen_header = b.addWriteFiles();     _ = gen_header.add(\"include\/banner_check.h\",         \\\\#pragma once         \\\\#include &lt;stdbool.h&gt;         \\\\bool check_user_proxy(const char* username, const char* remote_addr, char* proxy_ip_out);     );     b.getInstallStep().dependOn(&amp;gen_header.step);      \/\/ 6. \u0413\u0440\u0443\u043f\u043f\u0438\u0440\u0443\u0435\u043c \u0448\u0430\u0433\u0438 \u0441\u0431\u043e\u0440\u043a\u0438     const build_exe = b.step(\"exe\", \"Build only the executable\");     build_exe.dependOn(&amp;exe.step);      const build_lib = b.step(\"lib\", \"Build only the library\");     build_lib.dependOn(&amp;lib.step);      const build_test = b.step(\"test-build\", \"Build the test executable\");     build_test.dependOn(&amp;test_exe.step);      const build_all = b.step(\"all\", \"Build everything\");     build_all.dependOn(build_exe);     build_all.dependOn(build_lib);     build_all.dependOn(build_test);     build_all.dependOn(&amp;gen_header.step); } <\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0412\u043e\u0442 \u043a\u0430\u043a \u0442\u0435\u043f\u0435\u0440\u044c \u044d\u0442\u043e \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0438\u0437 C-\u043a\u043e\u0434\u0430:<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<p><strong>banner_check.h:<\/strong><\/p>\n<pre><code class=\"cpp\">#pragma once #include &lt;stdbool.h&gt; #include &lt;stddef.h&gt;  \/\/ for size_t  #ifdef __cplusplus extern \"C\" { #endif  external bool check_user_proxy(const char* username, const char* remote_addr, char* proxy_ip); external bool get_banner(const char* username, char* banner_out, size_t banner_len);  #ifdef __cplusplus } #endif <\/code><\/pre>\n<p>\u041f\u043e \u0441\u0443\u0442\u0438 \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0431\u044c\u044f\u0432\u043b\u044f\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0435\u043c \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0438\u0437 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.<\/p>\n<p><strong>worker-auth.c:<\/strong><\/p>\n<p>\u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0444\u0430\u0439\u043b\u0430, \u043f\u043e\u0441\u043b\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0445\u0438\u0434\u0435\u0440\u043e\u0432<\/p>\n<pre><code class=\"cpp\">#include \"banner_check.h\"<\/code><\/pre>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <strong>int post_common_handler(worker_st <em> ws, unsigned http_ver, const char <\/em>imsg)<\/strong> \u0432\u043d\u043e\u0441\u044f\u0442\u0441\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f:<\/p>\n<p>\u0412 \u0431\u043b\u043e\u043a\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0439 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445<\/p>\n<pre><code class=\"cpp\">char banner[4096], username[32], remote_addr[256], our_addr[256]; char proxy_ip[256] = {0};  memset(banner, 0, 256); memset(username, 0, 32); memset(remote_addr, 0, 256); memset(our_addr, 0, 256);<\/code><\/pre>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0431\u043b\u043e\u043a\u043e\u043c \u0441 \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0431\u0430\u043d\u043d\u0435\u0440\u0430 (\u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442 \u043a\u043e\u0434\u0430):<\/p>\n<pre><code class=\"cpp\">if (WSCONFIG(ws)-&gt;banner) { ...<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u0434:<\/p>\n<pre><code class=\"cpp\">int len; len = strlen(ws-&gt;username); memcpy(username, ws-&gt;username, len);  getsockname(ws-&gt;conn_fd, (struct sockaddr*)&amp;ws-&gt;our_addr, &amp;ws-&gt;our_addr_len);  ret = getnameinfo((void*)&amp;ws-&gt;remote_addr, ws-&gt;remote_addr_len, remote_addr, sizeof(remote_addr), NULL, 0, NI_NUMERICHOST); if (ret &lt; 0) goto fail; ret = getnameinfo((void*)&amp;ws-&gt;our_addr, ws-&gt;our_addr_len, our_addr, sizeof(our_addr), NULL, 0, NI_NUMERICHOST); if (ret &lt; 0) goto fail;  \/* Check if proxy is allowed *\/ if (!is_proxy_allowed(username, remote_addr, proxy_ip)) { const char *proxy_msg = \"\u0412\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f proxy ip: \"; size_t msg_len = strlen(proxy_msg); size_t ip_len = strlen(proxy_ip);          \/\/ Ensure we don't overflow the banner buffer if (msg_len + ip_len &lt; MAX_BANNER_SIZE + 32) {           memcpy(WSCONFIG(ws)-&gt;banner, proxy_msg, msg_len);           memcpy(WSCONFIG(ws)-&gt;banner + msg_len, proxy_ip, ip_len);           WSCONFIG(ws)-&gt;banner[msg_len + ip_len] = '\\0'; } else {           \/\/ If the message is too long, use a truncated version           const char *fallback_msg = \"\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u044b\u0439 proxy\";           memcpy(WSCONFIG(ws)-&gt;banner, fallback_msg, strlen(fallback_msg)+1); } } else { bool has_banner = get_banner(username, banner, sizeof(banner)); if (has_banner) {     memcpy(WSCONFIG(ws)-&gt;banner, banner, strlen(banner)+1); } else {     \/\/ something went wrong     goto fail;   } } <\/code><\/pre>\n<p><strong>is_proxy_allowed<\/strong> \u044d\u0442\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u043e\u0431\u0451\u0440\u0442\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u0432\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0432 \u043b\u044e\u0431\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0430:<\/p>\n<pre><code class=\"cpp\">bool is_proxy_allowed(const char *username, const char *remote_addr, char* proxy_ip) {   return check_user_proxy(username, remote_addr, proxy_ip); }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0441\u0431\u043e\u0440\u043a\u0438 ocserv:<\/p>\n<details class=\"spoiler\">\n<summary>\u041d\u0443\u0436\u043d\u043e \u043c\u043e\u0434\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0431\u043e\u0440\u043e\u0447\u043d\u044b\u0435 \u0441\u043a\u0440\u0438\u043f\u0442\u044b<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0412 <strong>configure.ac:<\/strong><\/p>\n<pre><code class=\"bash\"># \u041f\u043e\u0441\u043b\u0435 \u0432\u0441\u0435\u0445 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043f\u0440\u043e\u0432\u0435\u0440\u043e\u043a \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u043d\u043e \u043f\u0435\u0440\u0435\u0434 AC_CONFIG_FILES  # \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 Zig-\u043c\u043e\u0434\u0443\u043b\u044f AC_ARG_WITH([zig-module],   [AS_HELP_STRING([--with-zig-module=DIR],     [path to Zig proxy check module @&lt;:@default=..\/zig-module@:&gt;@])],   [zig_module_dir=$withval],   [zig_module_dir=..\/zig-module] )  AC_MSG_CHECKING([for Zig proxy check module]) if test -f \"${zig_module_dir}\/libbanner_check.a\"; then   AC_MSG_RESULT([yes])   HAVE_ZIG_MODULE=yes   ZIG_MODULE_LIBS=\"-L${zig_module_dir} -Wl,--whole-archive -lbanner_check -Wl,--no-whole-archive\"   ZIG_MODULE_CFLAGS=\"-I${zig_module_dir}\"   AC_SUBST([ZIG_MODULE_LIBS])   AC_SUBST([ZIG_MODULE_CFLAGS]) else   AC_MSG_RESULT([no])   HAVE_ZIG_MODULE=no fi  AM_CONDITIONAL([HAVE_ZIG_MODULE], [test \"$HAVE_ZIG_MODULE\" = \"yes\"])<\/code><\/pre>\n<p>\u0412 <strong>src\/Makefile.am<\/strong>:<\/p>\n<pre><code class=\"bash\">if HAVE_ZIG_MODULE ocserv_LDADD += @ZIG_MODULE_LIBS@ ocserv_worker_LDADD += @ZIG_MODULE_LIBS@ AM_CPPFLAGS += @ZIG_MODULE_CFLAGS@ endif<\/code><\/pre>\n<\/div>\n<\/details>\n<h4>\u041f\u043e\u0434\u0432\u043e\u0434\u043d\u044b\u0435 \u043a\u0430\u043c\u043d\u0438 \u043f\u0440\u0438 \u0441\u0431\u043e\u0440\u043a\u0435:<\/h4>\n<p>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0445\u0430\u043a \u043f\u043e \u0441\u0431\u043e\u0440\u043a\u0435: \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e zig \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a \u043f\u043e\u0434 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0440\u0430 \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0437\u0430\u043f\u0443\u0449\u0435\u043d, \u043a\u0430\u043a \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u044d\u0442\u043e\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0447\u0435\u0440\u0435\u0437 build.zig \u0435\u0441\u043b\u0438 \u0447\u0435\u0441\u0442\u043d\u043e \u043d\u0435 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0441\u044f \u043f\u043e\u043a\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0447\u0435\u0440\u0435\u0437 \u0441\u0442\u0440\u043e\u043a\u0443 \u0441\u0431\u043e\u0440\u043a\u0438.<\/p>\n<pre><code class=\"bash\">zig build install -Dcpu=baseline<\/code><\/pre>\n<p>\u041f\u0440\u0438 \u0441\u0431\u043e\u0440\u043a\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u0432\u0430\u0436\u043d\u043e \u043d\u0435 \u0437\u0430\u0431\u044b\u0442\u044c \u043f\u0440\u043e <code><strong>bundle_compiler_rt = true<\/strong><\/code>\u0432 build.zig, \u044d\u0442\u0430 \u043e\u043f\u0446\u0438\u044f \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0443 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0440\u0430\u043d\u0442\u0430\u0439\u043c \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0430 \u0432 \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b &#8212; \u0431\u0435\u0437 \u044d\u0442\u043e\u0433\u043e \u043b\u0438\u0431\u043e \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0441\u043e\u0431\u0440\u0430\u0442\u044c \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a \u043d\u0430 C, \u043b\u0438\u0431\u043e \u043e\u043d \u0432\u044b\u0439\u0434\u0435\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u043c \u043e\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0430 zig.<\/p>\n<h3>\u0418\u0442\u043e\u0433\u043e \u043e \u043f\u043e\u043b\u044c\u0437\u0435 \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430:<\/h3>\n<ol>\n<li>\n<p><strong>\u041f\u0440\u043e\u0441\u0442\u043e\u0442\u0430 \u0440\u0430\u0437\u0432\u0451\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u044f<\/strong>:<\/p>\n<ul>\n<li>\n<p>\u0421\u043e\u043a\u0440\u0430\u0442\u0438\u043b\u043e\u0441\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0444\u0430\u0439\u043b\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0451\u043c \u043a\u043e\u043d\u0444\u0438\u0433 \u0432 TOML (\u043c\u043e\u0436\u043d\u043e \u0438 \u0431\u0435\u0437 \u043d\u0435\u0433\u043e)<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0420\u0435\u0441\u0443\u0440\u0441\u043e\u0451\u043c\u043a\u043e\u0441\u0442\u044c<\/strong>:<\/p>\n<ul>\n<li>\n<p>\u041f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438: ~5MB (\u0432\u0435\u0441\u044c ocserv) \u043f\u0440\u043e\u0442\u0438\u0432 ~50MB \u0443 PHP-FPM<\/p>\n<\/li>\n<li>\n<p>\u0412\u0440\u0435\u043c\u044f \u043e\u0442\u043a\u043b\u0438\u043a\u0430: 3-5ms \u043f\u0440\u043e\u0442\u0438\u0432 50-100ms (\u0431\u0435\u0437 \u043e\u0441\u043e\u0431\u043e\u0439 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438)<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u041d\u0430\u0434\u0451\u0436\u043d\u043e\u0441\u0442\u044c<\/strong>:<\/p>\n<ul>\n<li>\n<p>\u041d\u0435\u0442 \u0432\u043d\u0435\u0437\u0430\u043f\u043d\u044b\u0445 \u043f\u0430\u0434\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>\u0427\u0451\u0442\u043a\u043e\u0435 \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u043e\u0448\u0438\u0431\u043e\u043a<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c\u043e\u0435 \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043f\u043e\u0434 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<h2>\u041f\u043e\u0441\u043b\u0435\u0441\u043b\u043e\u0432\u0438\u0435<\/h2>\n<p>\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e Zig \u0435\u0449\u0451 \u043d\u0435 \u0434\u043e\u0441\u0442\u0438\u0433 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f release, \u0438 <strong>\u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u0434\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u043d\u0430 \u0431\u043e\u043b\u0435\u0435 \u0441\u0432\u0435\u0436\u0435\u043c \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440\u0435 \u043f\u0440\u0438\u0434\u0451\u0442\u0441\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u043a\u043e\u0434<\/strong>.<\/p>\n<p>\u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b Zig \u0432\u0435\u0440\u0441\u0438\u0438: 0.14<\/p>\n<p><strong>P.s.<\/strong> \u0435\u0441\u043b\u0438 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u043b\u0441\u044f \u043f\u043e\u0441\u0442 \u2014 \u043f\u043e\u0434\u043f\u0438\u0441\u0432\u0430\u0439\u0442\u0435\u0441\u044c \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043c\u043e\u0435\u0433\u043e \u043f\u0440\u043e\u0444\u0438\u043b\u044f \u043d\u0430 \u0425\u0430\u0431\u0440 \u0438\u043b\u0438 \u043d\u0430 \u043c\u043e\u0439 <a href=\"https:\/\/t.me\/apl_architect\" rel=\"noopener noreferrer nofollow\">\u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c\u043c-\u043a\u0430\u043d\u0430\u043b<\/a>.<\/p>\n<p><strong>P.P.S.<\/strong> \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u043d\u0430\u044f \u043a\u0440\u0438\u0442\u0438\u043a\u0430 \u0438 \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0430\u0446\u0438\u0438 \u0432\u0441\u0435\u0433\u0434\u0430 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442\u0441\u044f<\/p>\n<p><strong>P.P.P.S \u0415\u0441\u043b\u0438 \u043d\u0435\u0442 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u0430 <\/strong>\u2014 \u043b\u0443\u0447\u0448\u0435 \u043d\u0435 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u0443\u0439\u0442\u0435.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><\/p>\n<div class=\"tm-article-poll-container\"><!--[--><\/p>\n<div class=\"tm-article-poll tm-article-poll_variant-bordered\">\n<div class=\"tm-notice tm-notice_positive tm-article-poll__notice\"><!----><\/p>\n<div class=\"tm-notice__inner\"><!----><\/p>\n<div class=\"tm-notice__content\" data-test-id=\"notice-content\"><!--[--><span>\u0422\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0443\u0447\u0430\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0432 \u043e\u043f\u0440\u043e\u0441\u0435. <a rel=\"nofollow\" href=\"\/kek\/v1\/auth\/habrahabr\/?back=\/ru\/articles\/921482\/&#038;hl=ru\">\u0412\u043e\u0439\u0434\u0438\u0442\u0435<\/a>, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430.<\/span><!--]--><\/div>\n<\/div>\n<\/div>\n<p><!--[--><\/p>\n<div class=\"tm-article-poll__header\">\u0410 \u0432\u044b \u043f\u0440\u043e\u0431\u043e\u0432\u0430\u043b\u0438 Zig<\/div>\n<div class=\"tm-article-poll__answers\"><!--[--><\/p>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">19.64% <\/span><span class=\"tm-article-poll__answer-label\">\u0414\u0430 (\u0437\u0430\u0448\u0451\u043b)<\/span><span class=\"tm-article-poll__answer-votes\">11<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 19.64%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent\">5.36% <\/span><span class=\"tm-article-poll__answer-label\">\u0414\u0430 (\u043d\u0435 \u0437\u0430\u0448\u0451\u043b)<\/span><span class=\"tm-article-poll__answer-votes\">3<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress\" style=\"width: 5.36%\"><\/div>\n<\/div>\n<\/div>\n<div class=\"tm-article-poll__answer\">\n<div class=\"tm-article-poll__answer-data\"><span class=\"tm-article-poll__answer-percent tm-article-poll__answer-percent_winning\">75% <\/span><span class=\"tm-article-poll__answer-label\">\u041d\u0435\u0442<\/span><span class=\"tm-article-poll__answer-votes\">42<\/span><\/div>\n<div class=\"tm-article-poll__answer-bar\">\n<div class=\"tm-article-poll__answer-progress tm-article-poll__answer-progress_winning\" style=\"width: 75%\"><\/div>\n<\/div>\n<\/div>\n<p><!--]--><\/div>\n<div class=\"tm-article-poll__stats\"> \u041f\u0440\u043e\u0433\u043e\u043b\u043e\u0441\u043e\u0432\u0430\u043b\u0438 56 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.   \u0412\u043e\u0437\u0434\u0435\u0440\u0436\u0430\u043b\u0438\u0441\u044c 6 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. <\/div>\n<p><!--]--><\/div>\n<p><!--]--><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/921482\/\"> https:\/\/habr.com\/ru\/articles\/921482\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u042f full-cycle \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u0440\u0430\u0431\u043e\u0442\u0430\u044e \u0441 Rust\/Zig\/Go \u0438 \u0443\u0432\u043b\u0435\u043a\u0430\u044e\u0441\u044c APL \u043a\u0430\u043a \u043e\u0441\u043e\u0431\u044b\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u043c\u044b\u0448\u043b\u0435\u043d\u0438\u044f \u043e \u043a\u043e\u0434\u0435. \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u044f \u043f\u0438\u0448\u0443 \u043d\u0430 \u0431\u043e\u043b\u044c\u0448\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u044f\u0437\u044b\u043a\u043e\u0432, \u043d\u043e \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u043d\u0435 \u043e\u0431 \u044d\u0442\u043e\u043c. \u0425\u043e\u0447\u0443 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043e\u0431 \u043e\u043f\u044b\u0442\u0435 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0438 \u0441 PHP \u043d\u0430 Zig \u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0435\u0451 \u0432 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a.  <\/p>\n<h3>\u041a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h3>\n<figure class=\"full-width\">\n<div><figcaption>\u041c\u043e\u0439 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0449\u0438\u043a \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432\u0430, \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u0431\u044b\u043b\u0430 \u0430\u0440\u0445\u0430\u0438\u0447\u043d\u043e\u0439: \u0440\u0430\u0441\u043f\u0430\u043a\u0443\u0439, \u0437\u0430\u043f\u0443\u0441\u0442\u0438 \u0441\u043a\u0440\u0438\u043f\u0442 \u0438 \u0442.\u0434. \u0417\u0434\u0435\u0441\u044c \u0432\u0441\u0451 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043e<\/figcaption><\/div>\n<\/figure>\n<p>\u041f\u043e \u0440\u0430\u0431\u043e\u0442\u0435 \u043c\u043d\u0435 \u0434\u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0431\u0438\u043b\u043b\u0438\u043d\u0433-\u043f\u0430\u043d\u0435\u043b\u044c \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f VPN-\u0430\u043a\u043a\u0430\u0443\u043d\u0442\u0430\u043c\u0438 \u043d\u0430 \u0431\u0430\u0437\u0435 OpenConnect (ocserv). \u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0431\u044b\u043b\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u043d\u0430 PHP \u0438 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u043b\u0430 \u0441\u043e\u0431\u043e\u0439 \u0442\u0438\u043f\u0438\u0447\u043d\u044b\u0439 legacy-\u043a\u043e\u0434:<\/p>\n<ul>\n<li>\n<p>\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0432\u043d\u044f\u0442\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b (\u0444\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u2014 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0445\u0430\u043e\u0441)<\/p>\n<\/li>\n<li>\n<p>\u041c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0440\u0430\u0437\u0440\u043e\u0437\u043d\u0435\u043d\u043d\u044b\u0445 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e \u0438 \u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u044e<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\">\n<div><figcaption>\u041f\u0440\u0438\u043c\u0435\u0440\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438, \u0441\u0442\u0440\u0435\u043b\u043a\u0430\u043c\u0438 \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0435\u043d\u044b \u043f\u043e\u0442\u043e\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445<\/figcaption><\/div>\n<\/figure>\n<h3>\u041f\u0435\u0440\u0432\u044b\u0439 \u044d\u0442\u0430\u043f \u043c\u043e\u0434\u0435\u0440\u043d\u0438\u0437\u0430\u0446\u0438\u0438: \u043f\u0435\u0440\u0435\u0445\u043e\u0434 \u043d\u0430 Go<\/h3>\n<p>\u0412 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e\u0433\u043e \u0431\u044e\u0434\u0436\u0435\u0442\u0430 \u044f \u043f\u0440\u043e\u0432\u0435\u043b \u0447\u0430\u0441\u0442\u0438\u0447\u043d\u044b\u0439 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433:<\/p>\n<ol>\n<li>\n<p><strong>\u0412\u044b\u043d\u0435\u0441 \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u044b\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u044b \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u043d\u0430 Go<\/strong>:<\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0441\u0440\u043e\u043a\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a<\/p>\n<\/li>\n<li>\n<p>\u0423\u0447\u0435\u0442 \u0442\u0440\u0430\u0444\u0438\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u0424\u043e\u043d\u043e\u0432\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u0427\u0442\u043e \u044d\u0442\u043e \u0434\u0430\u043b\u043e<\/strong>:<\/p>\n<ul>\n<li>\n<p>10-\u043a\u0440\u0430\u0442\u043d\u044b\u0439 \u0440\u043e\u0441\u0442 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>\u0421\u043d\u0438\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u043c\u044f\u0442\u0438 \u0432 5-7 \u0440\u0430\u0437<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u044b \u0432 \u0440\u0435\u0436\u0438\u043c\u0435 24\/7 \u0431\u0435\u0437 &#171;\u043f\u0440\u043e\u0441\u0435\u0434\u0430\u043d\u0438\u0439&#187; <\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<figure class=\"full-width\">\n<div><figcaption>\u041d\u043e\u0432\u0430\u044f \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f: \u0431\u0430\u043d\u043d\u0435\u0440\u044b \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u044b \u0432 ocserv, \u043f\u043e\u0434\u0441\u043e\u0431\u043d\u0430\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0430\u0437\u043d\u0435\u0441\u0435\u043d\u0430 \u043f\u043e \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u0430\u043c \u043d\u0430 go \u2014 \u0431\u0438\u043b\u043b\u0438\u043d\u0433 \u043f\u0430\u043d\u0435\u043b\u044c \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0437\u0430 UI, php \u0441\u0432\u0435\u0434\u0451\u043d \u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c\u0443 <\/figcaption><\/div>\n<\/figure>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u043e\u0431 \u044d\u0442\u043e\u043c \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u043d\u0435 \u0431\u0443\u0434\u0435\u043c, \u043d\u043e \u0435\u0441\u043b\u0438 \u0432\u0430\u043c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430 \u0442\u0435\u043c\u0430 \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0442\u044b\u043a\u0432\u044b \u0432 \u043a\u0430\u0440\u0435\u0442\u0443 \u2014  \u0434\u0430\u0439\u0442\u0435 \u0437\u043d\u0430\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445, \u043c\u043e\u0433\u0443 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0435\u0441\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0442\u0440\u0430\u043d\u0441\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u0432 \u0434\u0435\u0442\u0430\u043b\u044f\u0445 \u0438 \u043f\u043e \u0448\u0430\u0433\u0430\u043c.<\/p>\n<h3>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0431\u0430\u043d\u043d\u0435\u0440\u043d\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439<\/h3>\n<p><strong>\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 ocserv<\/strong>: \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0436\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u0430\u0441\u044c:<\/p>\n<ul>\n<li>\n<p>\u041f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>\u0420\u0430\u0437\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0433\u0440\u0443\u043f\u043f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>\u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e\u0431 \u043e\u0441\u0442\u0430\u0442\u043a\u0435 \u0434\u043d\u0435\u0439 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438)<\/p>\n<\/li>\n<\/ul>\n<h3>\u0420\u0435\u0448\u0435\u043d\u0438\u0435: \u0433\u0438\u0431\u043a\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0431\u0430\u043d\u043d\u0435\u0440\u043e\u0432<\/h3>\n<h4>\u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 PHP-\u0440\u0435\u0448\u0435\u043d\u0438\u0435<\/h4>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0441\u0442\u044b\u043b\u0438:<\/p>\n<ul>\n<li>\n<p>\u0422\u0440\u0435\u0431\u0443\u0435\u0442 php-fpm \u0438 \u0431\u044b\u043b\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e \u0441 PDO \u0432 \u0431\u0438\u043b\u043b\u0438\u043d\u0433 \u043f\u0430\u043d\u0435\u043b\u0438<\/p>\n<\/li>\n<li>\n<p>\u041d\u0443\u0436\u043d\u043e \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u043f\u0440\u0430\u0432\u0430\u043c\u0438 \u043d\u0430 \u0444\u0430\u0439\u043b\u044b \u0438 \u0441\u043e\u043a\u0435\u0442<\/p>\n<\/li>\n<li>\n<p>\u041f\u0435\u0440\u0435\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u0432\u0438\u0441\u0430\u043b\u043e<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u0433\u043b\u043e \u043d\u0435 \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0438\u0437-\u0437\u0430 \u043f\u043b\u043e\u0445\u043e\u0439 \u043f\u043e\u0433\u043e\u0434\u044b \u0437\u0430 \u043e\u043a\u043d\u043e\u043c<\/p>\n<\/li>\n<\/ul>\n<p>\u042f \u0431\u044b \u0441\u043a\u0430\u0437\u0430\u043b, \u0447\u0442\u043e \u044d\u0442\u043e \u0445\u043e\u0440\u043e\u0448\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0441\u0442\u0430\u0434\u0438\u0438 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f\u0430 \u2014 \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u043a\u0443, \u043d\u043e \u043d\u0435 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435.<\/p>\n<h4>\u041d\u043e\u0432\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0430 Zig<\/h4>\n<p>\u042f \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b:<\/p>\n<ol>\n<li>\n<p><strong>\u0421\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430<\/strong>\u00a0(\u043d\u0430 Zig):<\/p>\n<ul>\n<li>\n<p>\u0418\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0441 \u0431\u0430\u0437\u043e\u0439 \u0434\u0430\u043d\u043d\u044b\u0445<\/p>\n<\/li>\n<li>\n<p>\u041f\u0440\u044f\u043c\u0430\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0432 ocserv<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<li>\n<p><strong>\u041f\u0440\u0435\u0438\u043c\u0443\u0449\u0435\u0441\u0442\u0432\u0430 \u043d\u043e\u0432\u043e\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f<\/strong>:<\/p>\n<ul>\n<li>\n<p>\u0412\u0440\u0435\u043c\u044f \u043e\u0442\u043a\u043b\u0438\u043a\u0430: 3-5 \u043c\u0441 \u0432\u043c\u0435\u0441\u0442\u043e 50-100 \u043c\u0441<\/p>\n<\/li>\n<li>\n<p>\u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u043b\u0438\u0448\u043d\u0438\u0445 \u0444\u0430\u0439\u043b\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u0441\u0431\u043e\u0435\u0432 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0438\u0437-\u0437\u0430 \u0431\u0430\u043d\u043d\u0435\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u043d\u0438\u0437\u0438\u043b\u0430\u0441\u044c \u043d\u0430 70% (\u0437\u0434\u0435\u0441\u044c \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0441 \u0443\u0447\u0451\u0442\u043e\u043c \u0438 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u043b\u043e\u0433\u0438\u043a\u0438 \u043d\u0430 go)<\/p>\n<\/li>\n<li>\n<p>\u041a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u043d\u0446\u0438\u0434\u0435\u043d\u0442\u043e\u0432 \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u043b\u043e\u0441\u044c \u043d\u0430 100%<\/p>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>\u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0442\u0430\u043a\u0443\u044e \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0438 \u043d\u0430 C, \u043d\u043e \u044d\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0435\u0440\u0432\u044b\u0439 \u0448\u0430\u0433 \u0432 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u0438\u0441\u0442\u0440\u0438\u0431\u0443\u0442\u0438\u0432\u0430 \u0441\u0438\u0441\u0442\u0435\u043c\u044b. \u0414\u0430\u043b\u044c\u0448\u0435 \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0432 \u0432\u0438\u0434\u0435 \u043f\u043b\u0430\u0433\u0438\u043d\u0430, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u043d\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u043d\u044b\u0435 \u0447\u0430\u0441\u0442\u0438 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0432 \u043c\u043e\u0434\u0443\u043b\u0438 ocserv. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<ul>\n<li>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0443 \u0441\u0440\u043e\u043a\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a<\/p>\n<\/li>\n<li>\n<p>\u0423\u0447\u0451\u0442 \u0442\u0440\u0430\u0444\u0438\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438 \u0438\u0445 \u0441\u0435\u0441\u0441\u0438\u0439<\/p>\n<\/li>\n<li>\n<p>\u0418 \u0442.\u0434. <\/p>\n<\/li>\n<\/ul>\n<h2>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430 \u0438 \u0435\u0433\u043e \u043b\u043e\u0433\u0438\u043a\u0430<\/h2>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u0432\u044b \u043f\u043e\u0439\u043c\u0451\u0442\u0435, \u043f\u043e\u0447\u0435\u043c\u0443 \u044d\u0442\u043e \u043a\u0440\u0438\u0442\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430:<\/p>\n<ul>\n<li>\n<p>\u0412\u043e \u043f\u0435\u0440\u0432\u044b\u0445 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u0441\u0430\u043c\u0438 \u043f\u043e \u0441\u0435\u0431\u0435 \u043d\u0435\u0441\u0443\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u0430\u0436\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u0432\u0440\u043e\u0434\u0435: \u2014\u0412\u0430\u0448\u0430 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 \u0438\u0441\u0442\u0435\u043a\u0430\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 2 \u0434\u043d\u044f \u0438 12 \u0447\u0430\u0441\u043e\u0432 <\/p>\n<\/li>\n<li>\n<p>\u0412\u043e \u0432\u0442\u043e\u0440\u044b\u0445, \u0432 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u0438 \u043c\u0430\u043b\u0435\u0439\u0448\u0438\u0439 \u0441\u0431\u043e\u0439 \u0432 \u0441\u043a\u0440\u0438\u043f\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 (\u0438\u043b\u0438 \u0431\u0430\u043d\u0430\u043b\u044c\u043d\u043e \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0444\u0430\u0439\u043b \u0438\u043b\u0438 \u0441\u043e\u043a\u0435\u0442) \u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u2014  \u043d\u0443\u0436\u043d\u043e \u043b\u0438 \u0443\u0442\u043e\u0447\u043d\u044f\u0442\u044c, \u0447\u0442\u043e \u044d\u0442\u043e \u0443\u0440\u043e\u043d \u0434\u043b\u044f \u0431\u0438\u0437\u043d\u0435\u0441\u0430?<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\">\n<div><figcaption>\u0423\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u0440\u0430\u0431\u043e\u0442\u044b: \u0435\u0441\u043b\u0438 \u0433\u0434\u0435-\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0451\u0442 \u0441\u0431\u043e\u0439, \u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043a\u043b\u044e\u0447\u0451\u043d \u0431\u0435\u0437 \u043f\u043e\u044f\u0441\u043d\u0435\u043d\u0438\u0439 \u043f\u0440\u0438\u0447\u0438\u043d<\/figcaption><\/div>\n<\/figure>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 php-\u043a\u043e\u0434 \u043c\u043e\u0434\u0443\u043b\u044f:<\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"php\">&lt;?php  if (!true) die(\"-------- Test banner --------\");  $username = $_SERVER[\"USERNAME\"]; $site = '\/var\/www\/html\/panel'; \/\/ \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 chdir($site); require_once('include\/functions.php');  \/\/ \u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 (\u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u0434\u0430\u043d\u043e \u0438\u043b\u0438 \u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u043f\u0443\u0441\u0442\u044b\u043c) $orig_gl_message = $db-&gt;query(\"SELECT * FROM {{table}} LIMIT 1;\",\"global_message\",\"assoc\"); if (isset($orig_gl_message) &amp;&amp; $orig_gl_message['status'] == true)  $GLOBAL_MESSAGE = $orig_gl_message['message_text'];  \/\/ \u041e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0443\u043f\u043f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439.  $GROUPS = getGroupsWithUsers();  \/\/ \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f $GROUP_MESSAGES = getGroupMessages();  \/\/ \u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u044b\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 $INDIVIDUAL_MESSAGES = getIndividualMessages();  \/\/ \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043f\u043e \u0433\u0440\u0443\u043f\u043f\u0435 function getGroupMessage($username, $groups, $groupMessages) {     foreach ($groups as $group =&gt; $users) {         if (in_array($username, $users)) {             return $groupMessages[$group]; \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u044b         }     }     return null; \/\/ \u0415\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u043f\u0440\u0438\u043d\u0430\u0434\u043b\u0435\u0436\u0438\u0442 \u043d\u0438 \u043a \u043e\u0434\u043d\u043e\u0439 \u0433\u0440\u0443\u043f\u043f\u0435 }  \/\/ \u0424\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f function getIndividualMessage($username, $individualMessages) {     return $individualMessages[$username] ?? null; \/\/ \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0438\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u043e\u043d\u043e \u0435\u0441\u0442\u044c }  \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f $groupMessage = getGroupMessage($username, $GROUPS, $GROUP_MESSAGES); $individualMessage = getIndividualMessage($username, $INDIVIDUAL_MESSAGES);   $banner = \"\";  \/\/ \u0411\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0435 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f $_username = addcslashes($username, \"\\\\'\"); $return = $db-&gt;query(\"SELECT * FROM {{table}} WHERE login='{$_username}';\", \"users\", \"assoc\");  \/\/ 1\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442: \u0413\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 if (!empty($GLOBAL_MESSAGE)) {     $banner = $GLOBAL_MESSAGE; }  \/\/ 2\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442: \u0421\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0434\u043b\u044f \u0433\u0440\u0443\u043f\u043f\u044b elseif ($groupMessage) {     $banner = $groupMessage; }  \/\/ 3\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442: \u0418\u043d\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 elseif ($individualMessage) {     $banner = $individualMessage; }  \/\/ 4\u0439 \u043f\u0440\u0438\u043e\u0440\u0438\u0442\u0435\u0442: \u0421\u0440\u043e\u043a \u043a\u043b\u044e\u0447\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 elseif (isset($return[\"expire\"]) &amp;&amp; is_string($return[\"expire\"])) { $expire = @json_decode($return[\"expire\"], true); if(is_array($expire)) { $n = 60*60*24; $expire_timestamp = $expire[\"end\"]; \/\/ \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c timestamp \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f $expire_start_timestamp = $expire[\"start\"]; \/\/ \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c timestamp \u043d\u0430\u0447\u0430\u043b\u0430         $expire_seconds = $expire_timestamp - time(); \/\/$days = ($expire_seconds-$expire_seconds%$n)\/$n; \/\/ \u041d\u0435 \u043f\u043e\u043b\u043d\u044b\u0439 \u0434\u0435\u043d\u044c \u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u043f\u043e\u043b\u043d\u044b\u0439 (n + 1) $days = floor($expire_seconds \/ $n); \/\/ \u041e\u043a\u0440\u0443\u0433\u043b\u044f\u0435\u043c \u0432\u043d\u0438\u0437 \u0434\u043e \u0446\u0435\u043b\u043e\u0433\u043e \u0447\u0438\u0441\u043b\u0430 \u0434\u043d\u0435\u0439 $hours = floor(($expire_seconds % $n) \/ (60 * 60)); \/\/ \u0412\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u0447\u0430\u0441\u044b \u0438\u0437 \u043e\u0441\u0442\u0430\u0442\u043a\u0430 $start_date = date('d.m.Y', $expire_start_timestamp); \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u0442\u0443 \u043d\u0430\u0447\u0430\u043b\u0430 $today = date('d-m-Y'); \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0434\u0430\u0442\u0443 $expire_date = date('d.m.Y', $expire_timestamp); \/\/ \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0434\u0430\u0442\u0443 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f if ($days &gt;= 30 &amp;&amp; $days &lt; 31 ) { $messages = [ \"\u041f\u043e\u0437\u0434\u0440\u0430\u0432\u043b\u044f\u0435\u043c! \u0412\u044b \u0432\u044b\u0431\u0440\u0430\u043b\u0438 \u043b\u0443\u0447\u0448\u0435\u0435. \u0412\u0430\u0448 \u043d\u043e\u0432\u044b\u0439 \u0442\u0430\u0440\u0438\u0444 \u0443\u0436\u0435 \u0430\u043a\u0442\u0438\u0432\u0435\u043d \u0441 {$start_date} \u0433\u043e\u0434\u0430 \u0434\u043e {$expire_date} \u0433\u043e\u0434\u0430. \u041d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u0439\u0442\u0435\u0441\u044c \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u043e\u043c!\", \"Sizi gutla\u00fdarys! I\u0148 gowy tarifi sa\u00fdlady\u0148yz. T\u00e4ze tarifi\u0148iz {$start_date} -- {$expire_date} senesi i\u015fje\u0148dir. Howpsuz we \u00e7alt internetden lezzet aly\u0148!\"     ]; $banner .= $messages[array_rand($messages)];  } elseif ($days &gt;= 6 &amp;&amp; $days &lt; 7) { $messages = [ \"\u0423\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0435\u043c \u0432\u0430\u0441, \u0447\u0442\u043e \u0432\u0430\u0448 \u0442\u0430\u0440\u0438\u0444\u043d\u044b\u0439 \u043f\u043b\u0430\u043d \u0430\u043a\u0442\u0438\u0432\u0435\u043d \u0432 \u043f\u0435\u0440\u0438\u043e\u0434 \u0441 {$start_date} \u043f\u043e {$expire_date}. \u0416\u0435\u043b\u0430\u0435\u043c \u0445\u043e\u0440\u043e\u0448\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f!\", \"Size {$start_date} -- {$expire_date} seneleri aralygynda tarif planynyzy\u0148 i\u015fje\u0148digi barada habar ber\u00fd\u00e4ris. Ulany\u015fy\u0148yz rahat bolsun!\"      ]; $banner .= $messages[array_rand($messages)];            } elseif ($days &gt;= 1 &amp;&amp; $days &lt; 2) { $messages = [ \"\u0417\u0434\u0440\u0430\u0432\u0441\u0442\u0432\u0443\u0439\u0442\u0435! \u0421\u0440\u043e\u043a \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432\u0430\u0448\u0435\u0433\u043e VPN-\u043a\u043b\u044e\u0447\u0430 \u0438\u0441\u0442\u0435\u043a\u0430\u0435\u0442 {$expire_date}\u0433\u043e\u0434\u0430. \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0434\u043d\u0435\u0439: {$days} \u0438 \u0447\u0430\u0441\u043e\u0432: {$hours}.\", \"Hormatly agzamyz! Sizin VPN a\u00e7ary\u0148yzy\u0148 m\u00f6hleti {$expire_date} senesinde gutar\u00fdar. M\u00f6hletine {$days} g\u00fcn we {$hours} sagat galdy.\"     ]; $banner .= $messages[array_rand($messages)];                }elseif($days &gt;= 0 &amp;&amp; $days &lt; 1) { $messages = [         \"\u0412\u043d\u0438\u043c\u0430\u043d\u0438\u0435! \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0447\u0430\u0441\u043e\u0432: {$hours} \u0434\u043e \u0438\u0441\u0442\u0435\u0447\u0435\u043d\u0438\u044f \u0441\u0440\u043e\u043a\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432\u0430\u0448\u0435\u0433\u043e VPN-\u043a\u043b\u044e\u0447\u0430. \u041f\u0435\u0440\u0438\u043e\u0434 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f: \u0441 {$start_date}\u0433 \u043f\u043e {$expire_date}\u0433.\",         \"\u00dcns beri\u0148! VPN a\u00e7ary\u0148yzy\u0148 m\u00f6hletini\u0148 gutarmagyna {$hours} sagat galdy. M\u00f6hleti: {$start_date}-den {$expire_date}-e \u00e7enli.\"         ];     $banner .= $messages[array_rand($messages)]; }elseif($days &gt;= -3 &amp;&amp; $days &lt;= 0) { $messages = [         \"\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0438\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0432\u0430\u0448 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d \u0438\u0437-\u0437\u0430 \u043d\u0443\u043b\u0435\u0432\u043e\u0433\u043e \u0431\u0430\u043b\u0430\u043d\u0441\u0430.\",         \"Bagy\u015fla\u0148, h\u00e4zirki wagtda balansy\u0148yz nol bolany \u00fc\u00e7in hasaby\u0148yz bloklanyldy. \"         ];     $banner .= $messages[array_rand($messages)]; } } }  echo($banner); exit(0);<\/code><\/pre>\n<\/div>\n<\/details>\n<p><strong>\u041a\u0430\u043a \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e \u0432 \u043f\u043b\u0430\u043d\u0435 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 php \u0432 ocserv<\/strong><\/p>\n<p>\u0412 \u043f\u0430\u043f\u043a\u0443 \u0441 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c\u0438 \u043a\u043e\u0434\u0430\u043c\u0438 ocserv, \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f C-\u0445\u0438\u0434\u0435\u0440 <strong>fpm-client.h<\/strong>: <\/p>\n<details class=\"spoiler\">\n<summary>\u0421\u043a\u0440\u044b\u0442\u044b\u0439 \u0442\u0435\u043a\u0441\u0442<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">#include &lt;error.h&gt; #include &lt;errno.h&gt; #include &lt;sys\/socket.h&gt; #include &lt;sys\/un.h&gt; #include &lt;sys\/stat.h&gt; #include &lt;fcntl.h&gt; #include &lt;unistd.h&gt; #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt;  #include &lt;fastcgi.h&gt;  int fpm_client(char *fpm_socket_file_path, char *result, char *username) { int ret, len;  int unix_stream_socket; unix_stream_socket = socket(AF_UNIX, SOCK_STREAM, 0); if(unix_stream_socket &lt; 0) { error(0, errno, \"Can't create unix socket stream\"); return(-1); }  struct sockaddr_un addr; memset(&amp;addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; len = strlen(fpm_socket_file_path); memcpy(addr.sun_path, fpm_socket_file_path, len+1);  ret = connect(unix_stream_socket, (const struct sockaddr *) &amp;addr, sizeof(addr)); if(ret &lt; 0){ error(0, errno, \"Can't connect to unix socket stream\"); return(-1); }   FCGI_Header header; header.version = FCGI_VERSION_1; header.requestIdB1 = 0; header.requestIdB0 = 1; header.paddingLength = 0;  \/\/ FCGI_BEGIN_REQUEST  header.type = FCGI_BEGIN_REQUEST;  FCGI_BeginRequestBody begin_request_body; begin_request_body.roleB1 = 0; begin_request_body.roleB0 = FCGI_RESPONDER; begin_request_body.flags = 0;  len = sizeof(begin_request_body); header.contentLengthB1 = 0; header.contentLengthB0 = len;  ret = write(unix_stream_socket, &amp;header, sizeof(header)); if(ret &lt; 0) { error(0, errno, \"Can't write `begin request header`\"); return(-1); }  ret = write(unix_stream_socket, &amp;begin_request_body, len); if(ret &lt; 0) { error(0, errno, \"Can't write `begin request body`\"); return(-1); }  \/\/ FCGI_PARAMS  header.type = FCGI_PARAMS;  char *params[] = { \"SCRIPT_FILENAME\", \"\/etc\/ocserv\/banner.php\", \"REQUEST_METHOD\", \"GET\", \"USERNAME\", \"user\" }; params[5] =<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-466274","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/466274","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=466274"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/466274\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=466274"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=466274"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=466274"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}