Profile

coolwolf0: (Default)
coolwolf0

July 2025

S M T W T F S
  123 45
6789101112
13141516171819
20212223242526
2728293031  

Custom Text

Буквально вчера была окончательно решена проблема перманентного логина в Андроид-приложении.

Краткое содержание предыдущих серий.

1. Немного суровой реальности. Браузер общается с сайтом при помощи так называемой "сессии", то есть некоего набора данных, признаваемого обеими сторонами. В какой-то момент сессия считается просроченной и все данные о контактах браузера с сервером обнуляются. Если, например, сервер подтвердил ввод пароля для данной сессии, то потеряв сессию браузер не сможет утверждать серверу, что он имеет право совершать действия от имени пользователя. Естественно, народ уже давно решил эту проблему при помощи всяких исхищрений. Одним из них, например, является хранение контекста (или токена для его восстановления) в памяти браузера (кукис или локальное хранилище)

2. Ещё более суровая реальность. До сих пор мои сайты и андроид-приложения эксплуатировали эту фичу без зазрения совести. В webView кукис сохраняются так же, как в обычном браузере, достаточно подключить нужную функцию в классе приложения. Но вот настали трудные времена и все внезапно озаботились приватностью. Откладывать кукисы, даже для упрощения логина, оказалось страшно опасным действием. Все сайты вняли требованиям возмущённых пользователей и начали требовать от них добровольно соглашаться на сию потерю анальной девственности. В частности, мои сайты были занесены в "красный список" сами-понимате-какой-корпорацией именно за отсутствие "консента". Пройдя унизительную процедуру я вернул сайтам честное имя, но пользователям от этого лучше не стало. Периодически у них выскакивают напоминания "этот сайт следит за тобой, нажми кнопку чтобы стать его рабом". Логин при этом мягко говоря про%бывается и пользователю приходится совершать дополнительные телодвижения для его восстановления (да, без ввода пароля, но всё равно геморрой).

Какова же альтернатива? Я подумал тут с Gemini, и мы пришли к выводу, что можно хранить токен не в памяти браузера, а в приложении, передавая его туда-сюда через адресную строку. Для десктопного браузера это неприемлемо, ведь тем самым мы позволяем пользователю сверкать у всех навиду голой жопой секретным токеном. Но для смартфона такой опасности нет. Адресная строка в приложении отсутствует, а значит всё, что там лежит остаётся между приложением и сервером. Короче, страничка логина принудительно получает от приложения дополнительный параметр, означающий "это мобильный клиент, ему - можно". При удачном логине сервер делает для такого клиента исключение. Он генерит этому пользователю персональный токен, запомнинает его вместе с датой выдачи и возвращает его в виде аргумента при перенаправлении на основную страницу. А поскольку основная страница тоже рендерится приложением, оно потихоньку считывает этот токен и запоминает его. Но запоминает уже не в виде богомерзкого кукиса, а в своей родненькой памяти, о которой никакой гугель-шмугель не знает, и знать не должОн. Если в следующий раз ему попадётся основная страничка, он пороется в своей памяти и, при наличии там токена, впрыснет его в адресную строку, чисто на всякий случай. В результате, основная страничка, подгружаемая в единожды залогиненом приложении, гарантированно получит токен. Всё, задачка решена, можно удалять код, откладывающий кукисы, а с ним заодно и консент-контент!

Ну и на дессерт: как я решил проблему пароля для регистрации и логина. Основной опасностью тут является хищение пароля всякими сниферами. Так вот, при моей схеме пароль тупо не передаётся на сторону сервера (и соответственно там его нет). Даже если вы сидите на канале пользователя и прорвались через HTTPS, то всё равно увидите только чек-сумму, да и то не самого пароля, а чего-то наложенного на ещё что-то. Как это работает? Очень просто. При регистрации в браузер посылается "соль" которой посыпается пароль. После этого результат (в виде чек-суммы) отправляется на сервер и там сохраняется. Каждая попытка логина сопровождается генерацией дополнительного секретного компонента (назовём его "перец"). Браузер накладывает на введённый пароль и "соль", и "перец" перед тем как вернуть результат на проверку. Сервер вытаскивает "посоленную" чек-сумму для данного пользователя, накладывает на неё индивидуальный "перец" и сравнивает результат с предъявленным браузером. Подделать или зареверсить эту схему практически невозможно.

Дополнительная "фишка" - защита от поддельного запроса на аутентикацию. Дело в том, что в роли "перца" выступает юникс-тайм на данный момент времени (со стороны сервера). Если кто-то вздумает повторить запрос на логин со старым "перцем", то сервер его отвергнет, так как он уже "просрочен" по сравнению с последним успешным логином. Единственной лазейкой является полная имитация всех алгоритмов на стороне браузера, плюс кража секретной чек-суммы пароля (что является фактически кражей самого пароля).
Если быть точнее, то рыбы две. Во-первых, получить работающий проект на АндроидСтудио оказалось невыполнимым квестом. При создании нового проекта та же самая версия IDE упорно выдаёт webView который не может прочитать ни одну страничку ни с какого сайта. Тупо падает на необъяснимой ошибке и всё. Единственным способом изготовления нового приложения оказалось размножение всей директории и тотальное переименование переменных и директорий внутри. Как ни странно, после такого шаманства приложение спокойно серфит куда ни попадя. Всезнающий AI на вопрос "чо за нах?" только железными руками разводит и несёт какую-то чушь.

Вторая рыба - покрупнее. Это целый PHP и Апач к нему. Уже несколько раз я натыкался на ситуёвину, когда из браузера шлёшь запрос в PHP API, пытаешься распарсить полученный JSON, и ... вылетаешь по exception "illegal character". Особо зоркий глаз может при этом разглядеть в дампе полученного пакета некий мерзкий непечатный символ перед самым началом JSON-структуры. И что бы я ни делал в самом коде - ничего не помогает. Ни танцы с бубном насчёт заголовка, ни поиски вражеского символа в закодированном ответе, ничегошеньки. Как вариант, в старых проектах я отчаянно вырезаю непечатные символы уже на стороне JavaScript, что есть непотребство и чёрная магия. Так вот, оказывается при редактировании исходника на PHP в самом начале файла могут влететь те самые непечатные символы. Называются они BOM (на английском) и означают некий префикс для распознавания кодировки. Если удалить этот самый BOM из исходника, всё работает как положено. И вот тут возникает вопрос: АНАХ#Я? Почему ни интерпретатор PHP, ни многоуважаемый Апач эту дрянь не фильтруют? Кому нужна такая какашка? Может быть есть сценарий, когда BOM посылается в начале реального HTML? Может быть. Но блин, как жестоко при этом пролетают разработчики API! :-[
Сначала - краткое содержание предыдущих серий. Года два-три тому назад я сделал из говна и палок своё первое андроид-приложение. Поскольку тогда ни о каком генеративном AI речи быть не могло, пришлось использовать видео-тьюториалы, наговоренные какими-то индийскими умельцами. С горем пополам то приложение было доведено до нужной кондиции, а его сорсы сданы в архив. И вот настал день "Икс", когда заглючил сайт у моего бесплатного хостера. Перенос кода и базы данных на другого хостера показал, что дело таки действительно было в хостинге. Ну ладно, причина и решение найдены, как теперь переучить имеющееся приложение работать с другим сайтом? На прошлой неделе я буквально за два дня восстановил приложение путём переноса в оригинальный IDE (на самом последнем сорсы тупо не компилировались). Всё, казалось бы хеппи энд... Но суровая реальность отвергла моё половинчатое решение. Приложение не ставилось на реальном "железе", а после декомпиляции и шаманства с цифровой подписью - зависало и вылетало.

Пришлось начать весь цикл заново. Я, уже наученный горьким опытом, задавал своему механическому наставнику правильно поставленные вопросы, заранее отсекая всякие боковые сюжетные линии. Мы с ним решили выбросить в топку старую версию АндроидСтудио "Flamingo" и перейти к более продвинутой "Iguana". В ней я тупо прошёлся по инструкциям моего опытного советчика и действительно получил самую первую работающую версию. Вот только после попытки открыть сайт она печально зависала. Причём в лучших традициях - без сообщений об ошибках. Пришлось учить, где в эмуляторе можно подглядеть консоль приложения. Дальше стало значительно легче. Ошибка была сходу распознана умным Джемини и по его совету я просто вырубил проверку сертификатов (так как эмулятор пребывал в каком-то странном состоянии рассинхронизации с реальностью). Ещё несколько итераций, и приложений прекрасно сработало в дебаг-режиме. После этого я стал терроризировать железного сенсея насчёт создания APK с валидной цифровой подписью, что в конечном счёте и удалось сделать. Приложение заработало на моём смартфоне, и тут можно было бы поставить точку. Но я ещё чуть-чуть пошаманил, встроил перезагрузку страницы по свайпу вниз, добавил в приложение свою фирменную иконку, зум по нажатию двумя пальцами и наконец - передачу информации наружу через "поделиться". Последним штрихом была отмена мерзкого фона в заголовке окна, диктуемого Material3 - пришлось подружиться и с этой частью настроек.

В целом я более чем доволен. На всю бодягу ушло два вечера мозговых штурмов в компании с Джемини. Приложение полностью восстановлено и теперь, если понадобится, я могу сварганить ещё что-то в аналогичном стиле. Например приложение в котором будут лежать планы поездки за границу, со всеми маршрутами, достопримечательностями, расписаниями и документами. А то я задолбался рассылать всей семье ссылки на очередной план путешествия.
В далёком одна тыща девятьсот поцарапанном году, будучи молодым специалистом не нюхавшим пороху, я очень сильно увлёкся продукцией молодой (тогда ешё) и перспективной фирмы мелокософт. Их поделки были не сильно качественными, но по крайней мере имели логичную структуру и функционал. Поскольку смартфонов в те времена ещё не было (кроме экранных прототипов в "Звёздных войнах"), простому народу приходилось распечатывать книги на бумаге. Однако текстовый формат очень плохо ложился на офисные листы А4. Не будет же нормальный человек читать книгу перелистывая такие гигантские страницы. Может для технической документации это был приемлемый вариант, но томик "Мастера и Маргариты" выглядел бы в этом случае немного коряво. Короче, на визуальном бейсике для Ворда из 95-го офиса, я накатал простой и эффективный алгоритм, превращающий обычный текст в документ, разбитый на страницы по две колонки, которые в свою очередь раскладывались в "тетрадки" по несколько сложенных листов. В результате можно было получить вполне приличную книжку формата А5. Одако, с появлением следующей версии мсявного офиса, бейсик оказался тупо несовместим, поэтому мой замечательный скрипт полетел в мусор.

К чему это я пустился в воспоминания? Ведь (как известно) нельзя войти в одну и ту же реку дважды. Зато в дерьмо - запросто. Пару лет назад ВПС совершил неимоверный для своего возраста подвиг: без помощи AI (который тогда ещё пешком под стол ходил) сварганил андроид-приложение. При том, что уровень владения этой вашей джавой - даже хуже чем "читаю перевожу со словарём". Ну сварганил, и казалось бы всё, можно спокойно почивать на лаврах. Ан-нет. Мой бесплатный провайдер, обеспечивающий хостинг FreeRSS2, внезапно стал корёжить файлы при передаче клиенту. Причём случайным образом. Внешне это проявляется как потеря иконок у Fontawesome, отсутствие стилей CSS и даже скриптов JS. Сами понимаете, без этого окно апликации теряет свой основной функционал. Короче, нашёл я другой бесплатный cpanel-хостинг (не без труда, их сейчас не так уж много) и даже перенёс на него приложение с базой данных. А вот как переучить готовый APK на альтернативный хост? В принципе, для этого делают landing-page (что я немедленно и сотворил на своём основном сайте), но проблема переделки APK никуда не ушла.

Ну ладно, думаю, скачаю на новый лептоп Андроид-Студио, закину в него свой старый проект и вуаля. Увы, получилось не "вуаля", а совсем неприличное слово. Последняя версия Android Studio с фривольным названием Ladybug отказалась компилировать мой выстраданный Java-код. Каких-то зависимостей не хватило. Попытка пересоздать проект и скормить ему оригинальный MainActivity.java не продвинула меня никуда. Ошибки, бесконечные попытки совместить несовместимое и т.д. и т.п. Короче, покручинился добрый молодец и пошёл советоваться с любомудрым Gemini от компании Добра. Не горюй, говорит мне сей мудрый пенёк, есть у меня для тебя масса идеек. И как обычно, вывалил полный набор абстрактных советов с человеческих форумов, сопроводив общими рассуждениями о смысле жизни. Попробовал я самое очевидное - скачал старую версию Android Studio - ошибки перегруппировались, но компиляция всё равно вылетает. Я с этими ошибками к Gemini - он мне рассказывает сказки про миграцию на AndroidX, да про версии AndroidSDK. Что бы я ни делал - сплошные проблемы. Даже установка Android Studio Flamingo (завалявшейся в бекапе) не помогла.

Просидел я так допоздна, и завалился спать. А на утро добрая фея common sense подсказала мне, что последняя оставшаяся ошибка вообще-то относится к какому-то модулю тестирования. Нахрена оно мне в простейшем приложении, построенном на webView? Поменял я build variant с debug на release, и всё скомпилировалось, словно на дворе 2022 год.

Вы спросите, а где же мораль? Морали тут рассыпано куча. Начнём с того, что процесс сборки в джава-проектах мягко говоря неочевидный. И даже хвалёный Gradle мне мало помог. Файлы проекта оказались несовместимыми с новой версией IDE. Не в такой степени, как мелкософт бейсик, но всё равно. Ну и пользовательский интерфейс у этой "Студии" оказался весьма "на любителя". Задолбался я в нём искать элементарные вещи. Тот же Eclipse давно решил проблему поиска настроек - они все сведены в одно окно с удобной фильтрацией. На более чем 30-м году жизни мсявный офис тоже сделал что-то подобное. А вот корпорация Добра не шмогла. Ну и самая главная мораль: не для простых умов эта технология. Нехай молодёжь приложения под мобильный контент пишет. Мы уж как-нибудь обойдёмся Пайтоном, JS/Angular, PHP и SQL/Mongo.
Так уж получилось, что последний раз, когда мне довелось сваять настоящую графическую морду к программе, на дворе был где-то 2008 год (а может и ещё раньше). С тех пор как-то обходился консольными скриптами и веб-приложениями. Но вот приспичило автоматизировать некий процесс и чтобы всенепременно с полноценным гуём. И тут всё заверте...

Во-первых, старый как говно мамонта Tk выглядит сейчас мягко говоря неуместно. Соответствено, более продвинутый QT никак не прикручивается к Python2 (поскольку эта версия давно в "чёрных списках" у всех модулей). Закручинился добрый молодец, да видно некуда ему деваться - пришлось ставить самый продвинутый Пайтон - аж 3.12! К нему сразу же приставил QT5, ну и PyInstaller. Вот с этим "джентльменским набором" я и сваял простейший гуй к уже работающей консольной части. После компиляции (с помощью PyInstaller) получился полноценный виндовый экзешник, который сходу заработал на абсолютно постороннем для него компьютере. В процессе написанию кода пришлось пару раз сбегать на поклон к всезнающему Гугелю, а то в этом QT хрен знает какие методы для работы с виджетами (а ваш покорный слуга всё никак не поставит себе IDE более продвинутый чем gvim). В конечном итоге функционал вышел неплохой, а морда даже чем-то напоминает хакерские программки вроде кейгенов или инсталляторов вареза.

Да, если кому нужно - могу отдать за спасибо. Суть утилиты в обёртке вокруг ffmpeg: из ютьюб-ссылки извлекается её "секретный" плейлист m3u8 и вуаля - видео скачивается в нужном формате. Так что пишите письма ;-)
Всех причастных и сочувствующих поздравляю с 5784 годом. Если кто пропустил - ещё не поздно закрыть все долги перед всевышним и людьми - до Судного Дня.

У нас дома, помимо дочкиного дембеля, пошла череда перемен. Сгорел стаааренький кондиционер в салоне, помер относительно новый (11 лет) телевизор. Кроме того, мы наконец-то сподобились заказать у столяра нормальную тумбу под раковину в ванной, а заодно поменять (если хватит средств) и душевую кабинку. В минус к бюджету пойдёт и наша поездка по Португалии имевшая место в начале сентября. Если кто думает, что все эти статьи расходов я покрываю с зарпалаты, то увы. Пришлось продать немного акций (ещё до того, как вся биржа "покраснела").

Доча пошла на "авода мэудефет", то есть малопрестижную работу, за отработку которой министерство обороны выдаёт дембелям дополнительный грант. Из возможных вариантов был выбран кибуц где-то на границе с Иорданией. Место мягко говоря пустынное и малолюдное, кибуц (по сосалистической традиции) еле сводит концы с концами, но работа кипит. Сначала её временно поставили в теплицу на сбор помидорок-чери (где городское дитя успело два раза порезаться). Был вариант поработать на подъёмнике - срезать с пальм финики (сейчас как раз сезон), но она побоялась (и правильно). Остались пруды для разведения рыбы. Физически работа тяжёлая, но пока она справляется. Что характерно, на всех этих участках работают либо дети кибуцников, либо терпилы-дембеля, либо таиландские гастарбайтеры (привезённые по квоте). Вот такой колхоз.

Ну и немного о компьютерах ;-) У меня в приложении добавлена милая фича: выдача попап-напоминания о необходимости обновить RSS (для мобильной версии, как альтернатива пуш-сообщениям). Кстати, за две недели путешествия по Португалии и Испании приложение показало себя просто великолепно - никаких сбоев, словно я сижу дома и читаю новости. Однако, на днях хостер задумал коварное: апгрейд PHP с седьмой антикварной версии на более продвинутую восьмую. Сначала сайт просто не открывался, выдавая ошибку сервера. Потом на меня посыпались сообщения о проблемах с переменными и ключами массивов (при PHP7 такой фигни не было). Еле-еле выкосил эту фигню (если кому надо, решается через $some_var['some key'] ?? null), как обнаружилось, что системные часы на хосте уехали вообще непонятно куда. Исправил часы - начались блокировки хостером за подозрительную активность (редиректы ему не понравились, куссоммо). Я запаниковал, стал бекапить базу и переезжать на заранее подготовленные позиции альтернативного аккаунта (где PHP8 ещё не повалялся). Но вроде бы на текущий момент всё устаканилось (тьфу-тьфу-тьфу). Чего и всем желаю ;-)
Да простят меня читатели, но я о своём, о "компьютерном".
Если быть совсем точным, то это уже не компьютер, ибо ВПС наконец-то (к 55 годам) смог наваять приложение для ондроед-устройства, который как бы уже и не персоналка, а чёрт-те-что.
И хоть "программа" получилась на несколько строчек длиннее "Hello, world!", к тому же написанная под копирку с лекций каких-то индийских товаристчей, но тем не менее, из неё вышло полноценное приложение (открывающее во встроенном браузере захардкоденную ссылку на сайт).
Более того, я даже скопипастил туда две фичи: не выходить из приложения по кнопке "назад", пока не достигнут нулевой уровень переходов, а также выполнить "зум" при нажатии пальчиками в двух местах.
Жалко, что перезагрузка страницы (по свайпу вниз) оказалась мудрёной, а так - всё работает как будто действие происходит в обычном браузере.
В планах теперь допилить поддержку свайпа вниз и приделать некую среду компиляции+установщик. Логично было бы что-то вроде NPM, но по сути можно обойтись двухстрочными BAT-файлами. Тем более, что вопрос закачки по FTP я буквально недавно решил - с десктопа можно заливать файлы в пакетном режиме при помощи встроенного FTP-клиента от мелкомягких или (более кошерно) - официально-бесплатным клиентом WinScp.
Теперь окончательно решён вопрос переноса на андроид аггрегатора новостей FreeRSS2, а также семейного списка покупок my-shopping-list. Осталось только допилить код, чтобы можно было скачать приложение с главной страницы (но это уже на вебе).

UPD: нашёл и впендюрил перезагрузку странички по свайпу! При этом один проект покорёжился, так что пришлось его подымать из руин. Срочно бегу загонять сорсы в GIT...
Сегодня на территории зеркального аккаунта FreeRSS были проведены учения, максимально приближённые к боевой обстановке.
Учения проводились с целью отработки восстановления хостинга при прямом пропадании всего, нажитого непосильным трудом.
В ходе учений были проведены следующие мероприятия: полное копирование кода с перезаписью старых сорсов, полный дамп всей базы данных на донорском хостинге (силами собственного PHP-скрипта), удаление подчистую всей схемы на целевом хостинге и применение к нему дампа (практически вслепую, без правки исходников).

По результатам учений была получена оригинальная "морда" донорской версии и абсолютно те же данные. Командование от лица службы выражает само себе благодарность за портируемый, независимый код, позволяющий подцепить донорский дамп где угодно.

Но наши бойцы не останавливаются на достигнутом, тем более, что хостер категорически запретил доступ через API к приложению. Поэтому параллельно идёт работа над созданием JSON-дампа, который можно будет перенести с хостинга на хостинг без танцев с бубнами вроде скачивания по FTP и запуска SQL в веб-интерфейсе. Если получится, то в следующий отпуск смогу поехать уверенным в несокрушимости закромов RSS, вплоть до восстановления аккаунта в антисанитарных условиях, по WiFi, в какой-нибудь гробнице. Дополнительное преимущество этого подхода в том, что дистиллированные данные можно объединять с имеющимися при переносе туда-сюда (в отличие от полного дампа SQL).
Только я затронул тему "таинственного" поведения программ, как буквально сегодня "стрельнуло" в совсем неожиданном месте.
Итак, есть веб-страничка, генерируемая пе-ха-пе на стороне сервера, и на страничке имеются кнопки/ссылки вызывающие некую JavaScript функцию. Функция быстро-быстро что-то делает и обновляет страничку, дабы показать результат. Всё работало, пока не купила баба порося: захотелось мне перед выполнением функции переспросить пользователя, мол "ты точно уверен?".

Пруф-оф-концепт прекрасно отработал на одной веб-страничке и я смело перенёс тот же код на другую страничку с похожим функционалом. И вот тут началось: открывается окно, и сколько бы я ни кликал кнопку "отмена" - окно открывается снова и снова!
Что за фак, подумал я, и вставил отладочные печати в JavaScript код. Результат вышел просто феерический: по клику функция "переспросить" вызывалась снова, как в сказке про белого бычка! Попытки как-то стрейсить эту засаду не дали ничего: стек содержит какой-то анонимный источник, явно не из жабаскрипта.

Что делать? Не бросать же работающий прототип в топку. Я начал с другого конца, открыл DOM-модель и начал искать вызовы функции там. Что же обнаружилось? Тихий ужас: ссылки на пресловутый коллбек были расставлены буквально на всех уровнях DOM, начиная с основной кнопки-ссылки. По крайней мере стало ясно, откуда сдох бобик: куда бы я ни кликнул, соответствующий объект внезапно обнаруживал у себя onclick-событие и отрабатывал его вызовом многострадальной функции с открытием модального окна. Первой мыслью был некий вирус (ага, в собственном коде, генерируемом к тому же на пе-ха-пе со стороны сервера). По трезвому размышлению я отверг бредовый вариант, внимательно посмотрел на HTML-код и ... обмер: у тега <a href="..."> не было закрывающей части! Вот такой лес, без деревьев! А поскольку HTML - не язык программирования, там непарные конструкции - не более, чем причуда клиента, поэтому браузер прикидывается ветошью и ... продолжает отрабатывать незакрытый тег! В результате доблестная DOM-модель продолжила считать ссылкой весь код после пресловутой кнопки. Ну как тут не ругнуться на всех известных языках!?
Буду краток. Вы смеётесь над названиями в каталоге Икеи? Я сегодня обнаружил, что в стандартном SQL есть функция под названием ... COALESCE ! Возможно, что это какая-то аббревиатура, и аксакалы даже помнят, как она расшифровывается, но "эти люди запрещают мне ковырять в носу?!!".

В обсуждении на профильном форуме кто-то робко предложил использовать логично звучащую функцию IFNULL, но "зубры" его осадили: тут вам не балаган с MySQL диалектами, извольте изъясняться на стандартном SQL!

Как говорится, сабж...
 Я давно хотел звести эту рубрику. Во-первых, под таким заголовком можно сокрушаться по поводу чего-то уму нерастяжимого. А во-вторых, спрашивать у почтеннейшей публики, каким словом назвать то или иное явление.

В данном случае получился микс: как назвать уму нерастяжимую ситуацию. Итак по порядку. У программистов часто бывает "затык", когда правишь что-то, а оно никак не реагирует на исправления.

Пример номер один: в браузере открыта страничка с дебагируемым сайтом, в окошке сорсов программист исправляет что-то, но ничего не меняется. А причина проста - он работал с девелопмент-версией, а в браузере висела продакшн.

Пример номер два, немного более сложный: работая с программой на компилируемом языке, забываешь перекомпилировать после исправления, и тупишь в код, недоумевая, почему не видны изменения при выполнении?

Пример номер три, совсем страшный: находишь по тексту нужное сообщение об ошибке, исправляешь его, запускаешь правильную версию ... и нифигашечки ничего не изменилось - печатается старое сообщение. Это - классический антипаттерн: в коде имеется несколько мест, где печатается одинаковое сообщение, и (естественно, по закону подлости) под руку для исправлений попадается совсем не тот фрагмент кода.

Ещё один антипаттерн: какие бы исходные данные не передавались бы на входе, результат получается один и тот же. Причина: для результата берётся не та переменная, или что ещё более мерзко - на каком-то этапе значение переменной тупо переприсваивают, и к результату она бодро идёт с новым значением, позабыв про многострадальные исходные данные.

Ну и совсем тяжёлый случай: в десктопной версии изменения видны, а тот же код на мобилке хромает старыми костылями. Причина тут немного необычная, но вполне реальная: в десктопном браузере можно принудительно перезагрузить страничку, включая картинки и CSS, а вот мобильная версия таких опций не имеет, поэтому волочит за собой стааарый кеш.

Собственно вопрос: каким словом (кроме матерных) можно ёмко и ясно обозвать данную ситуацию? На иврите есть устойчивое выражение, взятое из анекдота про поиски денег под фонарём (хотя потеряли их в другом месте) "ми-тахат ле-панас". А как это будет по-русски (английский тоже сойдёт) ?

PS: как бороться с такими ситуациями? Ну во-первых, перед тем, как начать рвать на голове волосы (у кого остались), попробуйте преднамеренно испортить исходный код, чтобы он заведомо упал на синтаксисе. Если опять никакой реакции - идите по следу от исходников до результата, потому что дебагируется явно что-то не то. Для случая с копипастой кода: это мерзкий антипаттерн, бойтесь его как огня при написании/исправлении программы, а то он вам ещё и в других ситуациях "выстрелит", например, при правке бага. Нецелевое использование переменной поймать тяжело, но можно: в современных языках есть всякие приблуды для статического анализа кода - не ленитесь их запускать при каждом изменении и тщательно анализируйте результат. Ну и конечно же опасайтесь языков, в которых можно обратиться к переменной без объявления. Злостный затык с мобильным браузером тоже имеет решение: к каждому подгружаемому из HTML файлу в адресе надо приписать дополнительный аргумент, не меняющий сути (ведь картинки и CSS обычно не принимают аргументов), но зависящий от номера версии. Этот примитивный трюк заставляет браузер инвалидировать кеш для всех подключаемых к страничке объектов.
Короче, проект медленно но верно идёт от нулевой версии к первому официальному релизу. Дабы не позориться, ссылку на онлайн-версию пока не публикую. Посему (или засим) требуются добровольцы (или по-нашенски "митнадевки"), которые помимо получения пользы от продукта (чтение RSS с десктопа/смартфона), способны сформулировать какие-никакие замечания по дизайну, юзабилити, документации, UX/UI. Сразу скажу: денег нет, но вы держитесь (то есть платить за тестирование опен-сорса не могу, мне и самому не платят).

Я понимаю, что идея мягко говоря отстала от времени - сейчас почти то же самое можно почитать в той же телеге, но преимущества кастомной ленты никто не отменял. Можно произвольно построить свою "газету", разбив её на рубрики по типу источников (новости/блоги/варез), либо сформулировав некие фильтры, вылавливающие из общего потока статьи на определённую тематику.

Итак, если есть добровольцы - два шага вперёд. Адрес сайта перешлю в приватном сообщении. За проявленное мужество обещаю поощрить, насколько смогу (например, ссылками и благодарностями в доках).

PS: Если кто решится читать сорсы - не бросайтесь тапками, это пока PoC версия, не до красот. По-хорошему, там надо много чего перелопатить и причесать... Пока что главная цель - функционал.

КДПВ... )
Процесс регистрации прост и незатейлив, установка клиентской части на винде - тоже как два пальца об... ну вы поняли.

Несколько неудачно прошёл первый коммит (не в плане техники, а из-за моих развесистых ушей).
Дело в том, что до начала полноценной работы с сорс-контролом, я вбил явки-пароли подключения к MySQL в некий файл концигурации. Естественно, после деплоймента на сервак, для пользователя он невидим, но у меня хватило ума закоммитить его в GIT со всеми остальными сорсами.

Казалось бы, что за беда - удали файл и залей "пустышечную" версию... Но это если бы речь шла о статической системе. А сорс-контрол (как мстительный интернет) помнит всё! Так что никогда не повторяйте мою ошибку - коммитить можно только специально приготовленную копию проекта, без паролей и прочих чувствительных данных.
Короче, пришлось пересоздать проект (благо там было полторы версии) и заодно прикрутить самодельный патчер, чтобы "если чо" выпечь файл конфигурации из внешнего конфига.

Ну что я вам скажу, всё очень мило, кавайно и хайтечно. В наше время такой няшности не было (в далёком 97-м я только-только познакомился с RCS - по нынешним меркам это реальная жесть). Впрочем, о чём это я, - уже в антисоветской ОС NTS было понятие версии на уровне файловой системы, и там это работало как часы (я думаю, современные часы имеют столько же оперативки, сколько в том шкафе, размером с холодильник-другой). А другой опен-сорс проект я коммитил SVN-ом на флешку, сидя с лептопом в тамбуре поезда - это было лет 15 тому назад. Теперь всё онлайн, и можно не бояться внезапной потери хард-копии, будь то смерть диска или пропажа компьютера (тьфу-тьфу-тьфу).

Ещё чуть-чуть, и можно будет перейти от PoC версии к "нулевому релизу". От винта!

git stash
git update
git pull --rebase
git stash pop
... с хостером. Не пугайтесь, я на других фронтах раненый ;-)

Короче, хостер у меня строгий, но справедливый: на халяву разрешает только в бирюльки играться. Чуть какая важная функция - давай, бабки гони.
Но я и не претендую ни на что особенное: малюсенькая база MySQL (footprint практически не растёт, потому что накопить информацию вручную много не получится), какой-никакой PHP (не самая последняя версия, но и не старьё), доступ по FTP - вот и все требования.
И вот настал тот час, когда у хостера начали периодически выскакивать отлупы при соединении апачевской сессии с базой. В результате скрипт вылетает по мерзкому эксцепшену "Connection failed: SQLSTATE[HY000] [2002] Connection refused". Я его конечно же оттрассировал и попытался "поймать" через try ... catch, но проблема в том, что поймать можно что угодно, а потом-то чего прикажете делать? Попытаться тут же в цикле повторить соединение? Плохая идея, ибо в таких делах надо уметь держать себя в руках и дать второй стороне шанс успокоиться. Короче, в архитектурном плане стандартное решение - retry after delay (и так несколько раз, увеличивая длительность задержек).
Но, как оказалось после первой попытки, мой хостер тупо запретил sleep, usleep и вообще, любые попытки затянуть время обработки запроса. Я почесал лысую репу и выдал то, чему учат сисадмины: exec("ping -n 2 -i 1 127.0.0.1"); то есть "чеши яйца с интервалом в секунду". Второй раз забросил старик невод, и пришёл невод с той же тиной морскою - хостер строжайше запретил любые экзеки (хорошо хоть в ответ по морде не навешал).
Ладно, думаю, ты так, а мы - вот так! Запросы наружу по TCP работают? Значит им можно тайм-аут назначить? Ну вот тут-то ты и попался!

Короче, вот вам функция, которая ходит туда, незнамо куда, и отрубается в строго отсчитанный момент.

Read more... )

Скорее всего гугловский DNS не обидится, что ему шлют запросы на несуществующий URL. Я же его не DoS-ю, а так, в качестве "груши" использую, когда со мной родная база говорить отказывается.
Всё, это конец эпохи и начало новой. FreeRSS1, рождённый как упражнение в мультидисциплинарном программировании (веб, базы данных, обработка информации) окончательно ушёл в архив. С сегодняшнего дня я могу полноценно читать RSS-подписки со всеми фильтрами, поиском и обновлениями. Да, это "нулевая" версия, там конь не валялся в плане настроек, но уже работает аутентификация и даже добавление/удаление новых лент. Ну и самое главное - система полностью вышла в онлайн, нет необходимости держать включённым домашний комп и выставлять наружу открытый порт для доступа к аппликации извне.
В процессе интеграций я открыл для себя много интересных способов выстрелить себе в ногу. Например, сраный PHP не предупреждает о странном использовании неинициализированной переменной и молча выдаёт пустую строку там, где я ошибся на одну буковку в идентификаторе. При этом он мстительно игнорирует глобальные переменные (ибо антипаттерн).
Вообще этот язык лишь условно можно назвать языком программирования. Если ты не добрался на хостинге до 8-й версии, то названия встроенных функций у тебя выглядят как упражнение студента первого курса в именовании подпрограмм. Приёмчики работы с массивами напоминают какой-то Фортран, а не язык, используемый в 21-м веке. Короче, скоро закрою бек-эндовую часть проекта и возьмусь за GUI. Там по крайней мере будет веселее в плане дебага - можно хоть на пустышках в браузере тренироваться.
Я тут потихоньку ваяю код для полноценного аггрегатора RSS, чтоб базировался на вебе, с мордой от Bootstrap. Мой древний самопал бежит строго на локальной машине, а это бардак и каменный век с точки зрения доступа. Про внешнее оформление в стиле Веб-1.0 вообще молчу.
Уже сейчас имеется подкачка RSS-лент с сайтов по списку и умное хранение обновлений в базе, постраничное отображение статей, отметка прочитанного, редактирование кое-каких деталей имеющихся лент, поддержка клавиатурных шорткатов, плюс адаптивный дизайн под десктоп и смартфон.

Помимо всяких заморочек с базой данных, жабаскриптом и пе-ха-пе, приходится кое-где и велосипеды изобретать.
В частности, задумал я накатать свой алгоритм для шифрования и передачи пароля (как нового, так и при логине).
Сразу предупреждаю: мой проект не будет хранить никаких супер-пупер секретных или интимных личных данных.
Просто мне надо разграничить доступ между пользователями, ну и предотвратить возможный вандализм с угоном аккаунта, порчей или замусориванием данных, вот это всё... Код хотелось бы сделать открытым (но не содержимое базы данных, естественно).

Итак, задача номер один: как секьюрно передать первоначальный пароль в систему, с учётом того, что хакеры могут прослушивать траффик. Идея состоит в том, что я вообще не собираюсь принимать от пользователя какой-то пароль при регистрации. Он оставляет на сайте свой запрос и получает от меня первоначальный пароль мейлом. При этом я не храню сам пароль в системе, а только его чексумму.

Вторая задача состоит в том чтобы передать пароль зарегистрированного пользователя (с учётом того, что на этот раз хакеры точно смогут прослушивать траффик). Решение выглядит так: сначала клиентская часть отправляет в систему запрос "пользователь такой-то хочет передать пароль". Система генерирует временный кусок "соли" и пересылает его клиенту обратно. После этого клиентский жабаскрипт считает чексумму пароля, на неё накладывает кусок "соли", и полученный результат возвращает в систему. Система убеждается, что это та самая сессия, накладывает свою "соль" на свою сохранённую чексумму пароля и сравнивает её с полученным от клиента кодом.

Если хакер перехватит сообщения в обоих направлениях, то всё равно повторить запрос на авторизацию он не сможет - та сессия уже стёрта, значит новая временная соль не даст тот же результат, что и у законного пользователя, ведь у него не будет из чего изготовить первую чексумму. Увы, менять пароль при этом не получится, потому что новый текст придётся передавать по тому же незащищённому каналу.

Фактически, я разрубил гордиев узел несекьюрного канала при помощи вспомогательного канала (мейл).
Насколько мой сценарий опасен с точки зрения реальной жизни? Есть какие идеи?
Для тех, кто не в курсе: так программисты называют витиеватые способы, которыми программа может сделать себе больно.

Итак, был у меня замечательный код, просто конфетка: после выбора фильтра в верхнем меню он проходил по всем имеющимся объектам и помечал не прошедшие фильтрацию ноликом, а прошедшие - единичкой. После этого на сцену выходил его высочество AngularJS который показывал или прятал объект, в зависимости от того самого статуса.
Но начальство резонно заметило, что прятать от пользователя объекты, пусть даже отфильтрованные, некрасиво. А красиво было бы объект показать, но в стиле "disabled" - сереньким, с выключенными действиями по клику мышки. Сказано - сделано: пошаманил с CSS, переставил вместо ng-hide вычисляемый класс ng-class зависимый от всё того же битового поля 0/1.
И оно тоже прекрасно заработало, и увидел я что это хорошо, и сказал "да быть интеграции". Дело в том, что помимо этих визуальных чудес, я накрутил сложнючий код на PHP, который читал инфу из JSON-файла, объединял эту инфу с реальными объектами из базы данных, остатки разбивал на два подмножества (привет вычислительная математика)...
Поэтому, чтобы немного облегчить мне жизнь, вопросами фронт-энда занялся совсем другой человек. Он просто заметил, что в моём жаба-скрипте есть что-то связанное с условием показывать/спрятать, и логично рассудив, что именно этого ему почему-то не хватает, понаставил в собственном цикле единички во все объекты.
И вот, собрана бета-версия, начинаю тестировать и получаю полный игнор: что бы я ни фильтровал, ни черта не меняется. Начинаю дебажить жаба-скрипт - в нужные места программа заходит, нолики честно прописывает. Натравливаю на данные JSON.stringify и печатаю результат на консоль - та же картина, все нолики на месте. Но проклятый Angular продолжает игнорировать мои нолики! Уж не знаю, каким чудом я догадался заглянуть на несколько строчек ниже своего дампа: kurwa, вся моя фильтрация аккуратно идёт под нож. И ведь не упрекнёшь человека в том, что он мне данные попортил - надо было самому не лениться и переменную переименовать во что-то более self-explanatory.
Мой сайт для записи списка продуктов оказался внезапно очень важным для всей семьи. Ещё бы: без обзвонов, диктовки, согласования и прочей ботвы, каждый член семьи может зайти на сайт и обновить информацию: добавить продукт в список, удалить неактуальное, отметить покупку в режиме онлайн. Я знаю, что есть всякие гуглоформы и приложения, но у меня это работает именно как интегральный продукт: название товара сопровождается картинкой, список общий для определённых пользователей, весь функционал одинаково доступен как на телефонах/планшетах, так и в обычном десктопном браузере.

И всё бы хорошо, но хостится эта аппликация у бесплатных провайдеров, то есть безо всякого суппорта. Например, пока я был в Румынии, провайдер зачем-то заблокировал аккаунт. Судя по тикету, ему не понравилось, что я зарегистрировался с мейлом из домена rambler.ru (за что и был коллективно наказан). Ладно, тот инцидент улажен, но вот вчера случилось что-то совсем странное: база не просто упала, а "поломалась". Причём поломалась как-то странно: в консоли MySQL выскакивают предупреждения о сбое системных таблиц, но дамп и прочие запросы отрабатываются (и слава богу, иначе со старыми бекапами я бы вообще потерял лицо перед тёщей). Ну ошибки ошибками, а в какой-то момент и аппликация перестала логиниться к базе: пароль не принимается, выдаётся системная ошибка. Ошибку эту в нормальном мире решают как два пальца об асфальт, перезапуском mysqld, но я тут не хозяин, чтобы сервера перезапускать (а суппорта нет!)... Попытка создать новый аккаунт и накатить на него базу проваливается, ибо сервак у них общий, и он не сотрудничает со следствием.

Ну и конечно же, вся эта халабуда упала именно тогда, когда мы всей семьёй составили список, и жена с дочкой уехали по нему закупаться. А аппликация-то "ку-ку". Что делать? Гипс снимают, клиент уезжает! Но мы тоже не пальцем деланные: быстро (пока не получил от тёщи выговор с занесением) ищу аналогичный бесплатный хостинг, очень быстро создаю аккаунт, подымаю базу из бекапа, заливаю сорсы, подправляю файл конфигурации ... и всё работает! Это реально был случай экстремального администрирования.

Что характерно, параллельно с конвенциональным хостингом я позарился на хостинг от Амазона. Тот самый, знаменитый, облачный. Там всё такое из себя, пальцы веером, сопли пузырём, какие-то облака, виртуальные сервисы и прочие достижения технической мысли. Но вот поднять базу к бесплатному веб-аккаунту - низзя. Только за очень большие доллары, причём не в год, а в месяц! И мигрировать обычный MySQL бекап через их веб-форму мне тоже не удалось, ибо потребовалось заполнить какие-то формуляры, понасоздавать секретных сертификатов, ещё какой-то хрени... Ребяты, вы создаёте сервис для бесплатного хостинга или предоставляете мне доступ к материалам министерства энергетики США (ЕВПОЧЯ)? Почему бесплатный хостер сходу открывает мне доступ по FTP к моим сорцам, а вы разрешаете мне закачать ... ОДИН ФАЙЛ и только через веб-интерфейс? И если вы такие вумные, то почему нельзя просто задеплоить проект из чего-то локального (если я правильно понял, там даже github оказался не в почёте). Короче, обиделся я на них и вернулся в лоно обычных провайдеров. Более того, им я охотнее заплачу свои кровные доллары, потому что тёплое, LAMPовое, красивое.
По образованию и по специальности я - автоматизатор. Это значит, что всякие повторяющиеся ручные процессы бросают мне вызов не только в личном, но и в профессиональном плане.

Недавно жена приобрела у какого-то новомодного специалиста онлайн-тренинг по уходу за лицом - всякие упражнения, лекции, консультации. Формально за этот материал уплачено и она может им пользоваться, но только в течение года. А хотелось бы иметь все эти тренинги на компьютере, в телефоне и на телевизоре без ограничения времени.

Сначала я просто брал текст очередного урока в браузере, выделял нужную часть, правый клик и выбирал "печать в файл". Но уроки-то содержат ещё и видео... Я уже описывал в этом блоге, как можно сграбить видео с несильно защищённого сайта: находим ссылку на видеофайл в логе браузера и тупо скачиваем его. Но это был чисто "детский" пример. А вот в данном случае движок оказался чуть более продвинутый: проигрыватель работает с некой ссылкой типа *.m3u8 внутри которой имеется несколько ссылок на разные разрешения и ... всё. Скачать такой файл можно, но он представляет из себя какие-то линки на закодированные фрагменты видеопотока. Штирлиц призадумался и ... набрал в гугле "как скачать видео из файла типа NNN". Как ни странно, мой старый знакомый VLC player оказался первым кандидатом - у него есть режим записи потокового вещания (а m3u8 это как раз оно). Появилась первая надежда: скачав ссылку я скармливал её VLC и он честно гнал на выходе видос. Но это же куча ручной работы!

И вот тут на сцену выходит автоматизация. Для начала я написал bat-файл, который тупо скачивал основную ссылку, дабы получить второй файл. Попытался автоматически извлечь видео самого высокого разрешения и ... столкнулся с проблемой. Оказывается язык сценариев BAT в мелкомягкой среде сочиняли инопланетяне-дислектики. Для того, чтобы извлечь из файла последнюю строку там надо накатать какое-то нагромождение букв и символов, скорее напоминающее последнюю волю египетского фараона, а не "tail -1", как в юникс-шелле. Не буду тут цитировать сие страшное заклинание, дабы не пострадала психика читающих этот блог женщин и детей. Значит лежала моя дорога в казённый дом - скачал комплект юникс-утилит под винду, вставил знакомый "tail -1" в bat-файл и всё заработало: из первоначальной ссылки получался файл описания потокового видео. Но какая же это морока перетаскивать файлы в окошко, что-то там выбирать, сохранять, переименовывать, стирать... Короче говоря, у меня в чулане нашлась классная утилита ffmpeg, которой я в своё время что-то там перекодировал (по-моему FLAC в MP3). Так вот, она прекрасно подошла для автоматического сграбливания видеопотока! Всё, задача решена - землекопа полтора можно скачать видео, указав адрес потока из HTML сорса плейера и имя конечного видеофайла. Всё само скачивается, промежуточные файлы удаляются, благорастворение воздухов в облацех!
Для нового проекта в области DevOps учу кучу незнакомых вещей: все эти IPMI, DCMI, SNMP и прочая, и прочая.
В одной из под-задачек понадобилось проверить, а открыт ли https-порт (прежде чем ломиться туда с криками "сова, открывай, медведь пришёл!"). Насколько мне было известно, для тупых проверок нужен 'nc хост порт', вот только в автоматическом режиме это не работает. Попробовал стандартный флаг "-z" - в нашей версии "nc" его тупо нет (sic!).

Ну чо, скачал сорцы nmap, сконфигурировал, собрал и установил - voila, система заговорила на нужном языке! Совсем как в старые добрые времена ./configure; make; make install

А на другом конце пришлось собирать статистику причин отлупа удалённых машин. В текстовом виде всё относительно просто: суммируем по категориям, вычисляем проценты, печатаем табличку. Но начальство возжелало увидеть красивые круговые диаграммы. Почесал репу и ... за пару часов освоил генерацию диаграмм на Пайтоне.

I like my job |-)

КДПВ... )