Всякая мелочь одним постом-9

В заметке в ЖЖ «Бред сивой кобылы» собраны фотографии и описания советских клонов IBM PC.

В конце 80-х в СССР началась перестройка с ускорением. Может, если только ускорение, или только перестройка, и обошлось бы, но перестраиваться и ускоряться редкий Боливар такое вынесет… На фоне этого могучая электронная промышленность СССР и стран СЭВа, следуя руководящим документам эпохи осчастливила нас автохтонными ПК.

Итак сегодня маленький обзорчик советских клонов IBM PC — ПЭВМ.

На сайте «Мурмолка» есть интересная статья, вынесенная из закрытой «Лепры», про стажировку в Отделе Технического Контроля Управления Делами Президента.

Саша занимался Виброакустикой. Помните суперкрутые фильмы про шпионов, где звук снимали лазером с окна, либо направленным микрофоном с полукилометра, либо просто специальной штукой, прикладывающейся к стене?

Так вот, Саша был спецом по тому, как это делать или от этого защищаться. Нет, нам конечно же тоже рассказывали про побочные виброакустические возмущения, пьезоэффект и т. д. Но одно дело лекции, другое — слушать обсуждение вчерашнего футбольного матча, происходящее в здании напротив.

В ЖЖ Олега Лурье рассказ об расформированном в начале 90-х годов подразделении, занимавшимся спасением подводников, которое могло бы спасти тех, кто утонул в подлодке «Курск».

Глубина-то совсем небольшая — 104 метра минус тридцать метров сама лодка, итого получается семьдесят четыре- семьдесят два метра. Это же элементарно для моих ребят. Вся операция вывода подводников занимает около трех часов. Итого получается, что мы бы затратили на спасение оставшихся в живых моряков «Курска» не более 5—6 часов, а значит, какая-то, и, я думаю, немалая, часть подводников была бы спасена.

«Зачем исследовать космос» — перевод письма 1970 года отправленного монахине из Замбии, Марие Юкуда, заместителем директора по научной части в Центре космических полетов NASA, в ответ на вопрос о текущих исследованиях Марса.

В своем письме Вы спросили как я могу предлагать расходовать миллиарды долларов на путешествие на Марс, в то время, когда многие дети на Земле умирают от голода. Я знаю, что вы не ожидаете такого ответа, как «О, я и не знал, что есть дети, умирающие от голода, но теперь я буду воздерживаться от любых космических исследований пока человечество не решит эту проблему!»

На самом деле, я знал о голодающих детях задолго до того, как я узнал, что путешествие на планету Марс технически возможно. Тем не менее, я считаю, как и многие мои друзья, что путешествие на Луну и, в конечном итоге, на Марсе и другие планеты, это рискованное начинание, которое мы должны предпринять, и я даже считаю, что этот проект, в конечном счете, будет способствовать решению более серьезных проблем, с которыми мы сталкиваемся здесь, на Земле, чем многие другие потенциальные проекты помощи, которые обсуждались и обсуждаются год за годом, и которые очень медленно приносят осязаемые результаты.

Оставить комментарий

Многотарифные счётчики воды

Кто-нибудь ставил сабжевые счётчики? Всё ок, всякие ЖКХ не упираются рогом в какие-нибудь неведомые вчерамимжесочинённые бумажки?

4 комментария

Сжатие GIF через gzip

Эксперимент мой сжатию несжатых ГИФ, по сути, провалился — если статические изображения иногда оказываются меньше (или сравнимы) с PNG, то с анимированными изображениями, ради которых всё и затевалось, всё плохо, я не сумел подобрать условия, при которых новоявленный метод обгоняет обычные соптимизированные ГИФы.

Зато я попутно выяснил, что ГИФ, вопреки моим ожиданиям, иногда жмётся gzip, на первой попавшейся анимации я получил выигрыш порядка 9%. Так что вот он метод ещё больше утоптать файлы ГИФ:

#!/bin/sh

which -s defluff
if [ $? -eq 0 ]; then
    gifsicle -O3 "$1" |  gzip -9nc | defluff 2>&- > "$2".temp
else
    gifsicle -O3 "$1" |  gzip -9nc > "$2".temp
fi

size=( $(ls -l "$2".temp) )

# срезаем заголовок gzip и контрольную сумму
let 'size=size[4]-10-8'

dd if="$2".temp of="$2" bs=1 skip=10 count=$size 2>&-
rm -f "$2".temp

В настройки Апача надо добавить новое расширение файла:

AddEncoding deflate .gif-def
AddType image/gif .gif-def

Для браузера вместо gzip я указываю метод deflate — по факту это тот же gzip, но без заголовка и контрольной суммы, что позволяет сэкономить 18 байт, не бог весть сколько, конечно, но почему бы их не убрать, спрашивается.

Утилита defluff , которая у меня упоминается — это оптимизатор кодов Хаффмана для gzip, PNG и zip, иногда даёт выигрыш в размере.

25 комментариев

Конвертор в несжатый GIF

Сделал я конвертор из обычного ГИФа в несжатый формат сжатый gzip, выложил на ГитХабе. Основное (разбор ГИФа) написано на Пайтоне, утилита, которая всё запускает — на Шеле. Для работы требуются ImageMagick (предпочтительнее) или gifsicle, gitinter (из libungif) и интерпретатор Пайтона.

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

Из плюсов для себя: детально для себя разобрал формат GIF, освежил знания Пайтона (которые уже начинают тускнеть), Шела, поработал над интересной идеей.

11 комментариев

Прогресс по несжатому ГИФу

Написал половину требуемого, чтобы попробовать эффективно конвертировать анимированный ГИФ в несжатый формат — разбор ГИФа на Пайтоне. Сначала я раскладываю ГИФ на информационные блоки, потом дербаню на кадры при помощи gifsicle, прогоняю каждый через gifinter и вытаскиваю (той же программой на Пайтоне) бинарное содержимое картинки.

Работает медленно, но мне больше нужна наглядность, а не скорость, с тем рассчётом всё и писал — вынимаю почти максимум, большинство данных даже не использую. На анимации в 47КБ (23 кадра) работает 4 секунды на моём слабеньком ноуте. Львиную часть времени (≈75%) занимает выемка бинарного тела картинки.

Вечером буду писать штуку, которая упаковывает полученное обратно в ГИФ.

Ни один браузер, кстати, не умеет отображать всё, что написано в стандарте ГИФа.

Оставить комментарий

Поддержи антитабачный закон!

Попросили опубликовать. Поскольку я поддерживаю эту инициативу, с удовольствием у себя публикую (ниже текст не мой).

Антитабачный закон (10.42КиБ)

Интернет-демократия в действии! Газета «Московский Комсомолец» начала сбор подписей в поддержку законопроекта «О защите здоровья населения от последствий потребления табака». Законопроект уже поддержали профессор Леонид Рошаль, депутат Госдумы Николай Валуев, врачи-онкологи со всего мира.

Согласно предвыборной программе Владимира Путина, законопроект, собравший 100 000 подписей в Интернете, будет рассмотрен властями. Подписи, собранные на сайте МК, будут переданы в Государственную Думу и Правительство РФ.

При нынешнем уровне потребления табака Россия теряет около 400 000 человек из-за связанных с курением причин ежегодно. Эти смерти можно предотвратить, вводя эффективные меры борьбы с курением. Например, полный запрет курения в общественных и на рабочих местах позволит сократить количество госпитализаций из-за инфарктов на 30% в первые три года после вступления запрета в силу.

По данным Всероссийского опроса взрослого населения об отношении к мерам борьбы с табакокурением, 95% россиян считают курение вредным (72%, включая курильщиков, говорят, что это очень вредно). 80% населения страны поддерживают национальную политику борьбы с табачной эпидемией. В том числе за запрет на рекламу табачных изделий высказались 86% опрошенных, за финансирование программ профилактики потребления табака выступают 85%. Более действенные предупреждения на пачках сигарет нужны 81% россиян. Полный запрет на курение в общественных местах и на рабочих местах, включая рестораны и бары, поддерживают 82% жителей страны.

Поддержи антитабачный законопроект Минздрава! Поставь свою подпись на сайте «МК» и расскажи об этой акции друзьям!

42 комментария

Сжатая несжатая ГИФ-анимация

В догонку к предыдущей статье: мне удалось сделать несжатый анимированный ГИФ c сжатием через DEFLATE, вот он ниже (анимация не закциклена):

Шесть цифр на белом фоне: 1, 2, 3, 3, 2, 1.

Там шесть несжатых кадров (цифры «1», «2», «3», «3», «2» и «1») с небольшой задержкой, таблица из 16 цветов, в этом формате (несжатый ГИФ+DEFLATE) файл занял 619 байт:

bolk@Bolk ~  $ telnet bolknote.ru 80
Trying 91.230.61.15...
Connected to bolknote.ru.
Escape character is '^]'.
HEAD /imgs/2012.08.08.1.gif-gz HTTP/1.0
Host: bolknote.ru
Accept-encoding: gzip

HTTP/1.1 200 OK
Content-Type: image/gif
Content-Length: 619
Content-Encoding: gzip

Собранная при помощи gifsicle анимация в формате «обычного» ГИФа занимает 1028 байт, разница в полтора раза. Права, я выключил в gifsicle оптимизацию — иначе gifsicle исключит из изображений повторящиеся части и эксперимент будет нечистым.

17 комментариев

Сжатый несжатый GIF

Как многие знают, браузер нативно понимает сжатие gzip (алгоритм DEFLATE). Наконец-то браузеры научились с ним правильно работать, после стольких лет мучений — сжимать можно что угодно, достаточно просто указать правильный заголовок.

Графический файлы обычно так не сжимают — внутри у них свой алгоритм сжатия, например в ПНГ используется тот же DEFLATE. Но с ГИФом ситуация хуже — у него внутри более старый алгоритм LZW, к тому же нет никаких оптимизационных техник (например, ПНГ использует так называемые «фильтры», которые позволяют эффективно сжимать градиенты.

Вот я и подумал — нельзя ли как-то заменить алгоримт сжатия ГИФа с внутреннего LZW на внешний gzip? Если было бы нельзя, я бы эту заметку не писал.

Смотрите, слева у вас на экране ГИФ, сжатый при помощи gzip (так и грузится с сервера), справа — ПНГ.

Пиксельная снежинка с розовыми, оранжевыми и бирюзовыми лучами

Пиксельный узор в виде цветка с розовыми, оранжевыми и бирюзовыми лепестками на белом фоне.


В каждом из них равное количество уникальных цветов, ПНГ пропущен через все оптимизаторы, которые я только нашёл. При этом ПНГ занимает 1374 байта, а гзипованный ГИФ — 1347, на 27 байт меньше. Дело тут конечно, не в паре десятков байт, а в принципе — если бы я выбрал картинку побольше, выигрыш, возможно, был бы существеннее.

Почему же LZW внутри ГИФа, плюс DEFLATE снаружи сделали файл меньше, чем оптимизирующие просто DEFLATE, плюс фильтры в случае ПНГ? Обычно наложение одного сжимающего алгоритма на другой делает файл больше сравнению с применением одного, более удачного алгоритам из двоих.

Дело в том, что в ГИФ я использовал несжатый, он подготовлен специальным образом, так, что его собственный алгоритм сжатия не работает, а gzip работает над несжатым потоком данных.

Самый простой, по моему мнению, способ получить такой файл — пропустить его через утилиту gifinter из набора libungif — библиотеки и набора утилит для работы с несжатыми файлами ГИФ.

Утилиты эти отлично скомпилировались под мой «Мак» и обрабатывал я ГИФ следующим образом: для начала оптимизировал его при помощи giflite (эта старая утилита под DOS, которую я запускаю из-под dosbox, творит с ГИФами чудеса), потом пропустил через утилиту, вырезающую из ГИФа комментарии (их туда вставил giflite), только после этого пропустил файл через gifinter и сжал при помощи gzip:

gifinter optimized.gif > non-compressed.gif
gzip -9n non-compressed.gif

Дальше я переименовал файл в 2012.08.08.gif-gz и добавил в конфигурацию своего «Апача» следующие строки:

AddEncoding gzip .gif-gz
AddType image/gif .gif-gz

Теперь при запросе сервер будет выдавать все необходимые заголовки:

bolk@Bolk ~  $ telnet bolknote.ru 80
Trying 91.230.61.15...
Connected to bolknote.ru.
Escape character is '^]'.
HEAD /imgs/2012.08.08.gif-gz HTTP/1.0
Host: bolknote.ru
Accept-encoding: gzip

HTTP/1.1 200 OK
Server: nginx
Date: Wed, 08 Aug 2012 06:12:14 GMT
Content-Type: image/gif
Content-Length: 1347
Connection: close
Accept-Ranges: bytes
Cache-Control: max-age=2592000
Expires: Fri, 07 Sep 2012 06:12:14 GMT
Content-Encoding: gzip

Если интересно как устроен несжатый ГИФ, об этом можно прочитать в Википедии, откуда я взял картинку, там интересная уличная магия, перепечатывать «Википедию» не очень-то интересно.

Готовой разгадки почему сжатый несжатый ГИФ обогнал ПНГ у меня нет, но есть одно возможное соображение — ГИФ чуть-чуть компактнее ПНГ, кроме того, ГИФ сжат целиком, тогда как в ПНГ сжата только картинка. Если моё предположение правда, то хорошего выигрыша ждать не сто́ит, но списывать формат ГИФ со счетов тоже не нужно.

По-настоящему опыт ценен, если удасться сделать несжатую ГИФ-анимацию (этого я ещё не пробовал) и сжать её при помощи gzip. Если удасться, это будет очень полезно, буду держать вас в курсе.

14 комментариев

Динозавр в храме Ангкор-Ват

Храм Ангкор-Ват (អង្គរវត្ត) в Камбодже известен, кроме своей нереальной красоты о которой как-нибудь в другой раз, стегозавром в барельефе одного из храмов. Мы там были в мае, всем туристам его обязательно показывают, показали и нам.

Причём окружающие животные нарисованы очень натурально, безо всяких фантазий, а тут — динозавр. У меня такой вопрос — не знает ли кто-нибудь научную теорию о том откуда он там взялся? Интернет завален личными мнениями, которые мне не интересны.

Каменная резьба с изображением стегозавура в круглом медальоне, окружённого растительным орнаментом и фигурой змеи.

Что ещё интересно, стегозавр нарисован ровно так, как будто художник его видел, т. е. без устрашающих излишеств, которыми снабжали чудовищ на старинных гравюрах.

15 комментариев

Льняная свадьба

Мужчина в жилете и бабочке и женщина в синем платье с перьями на шляпе сидят в креслах у стола с букетом и серебряным подносом, на стене — картина и лампа.

Думаю, зря мы забросили традицию отмечать годовщину свадьбы фотосессией, это интереснее, чем просто сходить в ресторан, как мы сделали в прошлом году. Льняную свадьбу (четыре года) мы отметили в стиле начала двадцатого века — революция ещё не случилась, но многие замечательные вещи — электрическое освещение, телефон уже вошли в нашу жизнь.

Съёмки проходили в доме-музее Арбузова, спасибо сотрудницам музея — Наталье и Маргарите, которые позволили провести фотосессию в этом замечательном музее и даже надеть некоторые вещи! Платье жене и бо́льшую часть моего костюма (один из сюртуков мы позаимствовали у музея) сшила непревзойдённая Рашида Сахипзянова (у неё есть анкета во «Вконтакте»).

За фотографии традиционное спасибище великолепной Маше Зверевой, остальные фотографии можно посмотреть у неё в ЖЖ (из тех, что она сама выбрала).

Также спасибо за трость (видели бы вы как я её красил из балончика с краской) Индире Файзрахмановой, за часы Елене Козаковой и Мире Абраровой, Лилии Ясиной за букет и Инне Каражаковой за причёску жены.

Жена, ты такая молодец, что смогла это всё организовать!

19 комментариев

Код для защиты GIF, PNG и JPEG (к предыдущей статье)

В коментариях к предыдущей заметке о моём предложении по защите графических форматов от внедрённого кода один из читателей попросил привести код, который показывал бы как моё предложение работает.

Для форматов GIF, JPEG и PNG я за полчаса написал следующее (язык — PHP 5.4 и выше):

<?
// //bolknote.ru август 2012
// Защищаем изображения, возращает false в случае неудачи
function ProtectPicture($inname, $outname)
{
    // типы изображения, которые мы можем защитить и функции защиты
    $enabled = [
        IMAGETYPE_JPEG => 'Jpeg',
        IMAGETYPE_PNG  => 'Png',
        IMAGETYPE_GIF  => 'Gif',
    ];

    if (list(,, $type) = @getimagesize($inname)) {
        if (isset($enabled[$type])) {
            if ($fi = @fopen($inname, 'rb')) {
                flock($fi, LOCK_SH);

                if ($fo = @fopen($outname, 'wb')) {
                    flock($fo, LOCK_EX);
                    // если все файлы удалось открыть, то вызываем функцию защиты
                    $ret = call_user_func('ProtectPicture'.$enabled[$type], $fi, $fo, filesize($inname));

                    fclose($fo);
                    fclose($fi);

                    if (!$ret) {
                        unlink($outname);
                    }

                    return $ret;
                } else {
                    fclose($fi);
                }
            }
        }
    }
    
    return false;
}

/* Private functions */

function ProtectPictureJpeg($fi, $fo, $controllen)
{
    // заголовок JPEG
    if (!@fwrite($fo, fread($fi, 2))) return false;

    $protect = '<?php __halt_compiler();';
    $length  = pack('n', strlen($protect) + 2);

    // дописываю секцию COM (comment)
    if (!@fwrite($fo, "\xFF\xFE$length$protect")) return false;

    // остаток файла
    if (@stream_copy_to_stream($fi, $fo) != $controllen - 2) return false;

    return true;
}

function ProtectPicturePng($fi, $fo, $controllen)
{
    // заголовок PNG
    if (!@fwrite($fo, fread($fi, 8))) return false;

    // пропускаем кусок IHDR (он должен быть первым))
    $len  = unpack('N', $lenbin = fread($fi, 4))[1] + 8;
    $ihdr = $lenbin . fread($fi, $len);

    // Смотрим, не написал ли в этот кусок хакер свой код
    if (($pos = strpos($ihdr, '<?')) !== false) {
        // в стандарте перечислены поля, которые есть в этом куске — там должно быть 13 байт (+4 — длина)
        if ($len != 17) return false;

        // эта комбинация может встретиться только в первых восьми байтах заголовка — дальше поля не
        // могут принимать такие большие значения
        if ($pos > 11) return false;

        // если разрешены короткие теги, нужно присмотреться к этому коду
        // в столь ограниченном пространстве вред можно натворить только через запуск
        // чего-то внешнего
        if (ini_get('short_open_tag') && strpos($ihdr, '`', $pos + 2) !== false) {
            return false;
        }
    } 

    if (!@fwrite($fo, $ihdr)) return false;

    // кусок iTXt (text) с нашим кодом
    $protect = "<?php __halt_compiler();";
    $length  = pack('N', strlen($protect));
    $sum     = pack('N', sprintf('%u', crc32($protect)));

    if (!@fwrite($fo, "{$length}iTXt{$protect}{$sum}")) return false;

    // остаток файла
    if (@stream_copy_to_stream($fi, $fo) != $controllen - 8 - 4 - $len) return false;

    return true;
}

function ProtectPictureGif($fi, $fo, $controllen)
{
    $protect = "<?php __halt_compiler();";

    // заголовок GIF
    if (!@fwrite($fo, fread($fi, 6))) return false;

    // поле Logical Screen Descriptor
    $lsd  = fread($fi, 7);
    $flag = ord($lsd[4]);

    // указана ли Global Color Table?
    if ($flag & 128) {
        $pixel = 1 + ($flag & 0b111);
        $colors = pow(2, $pixel);
        $gctlen = $colors * 3;

        // читаем Global Color Table
        $gct = fread($fi, $gctlen);

        // не вставил ли уже туда злоумышленник „<?“?
        // меняем у такой комбинации последний бит — это малозаметно,
        // безопасность важнее!
        if (strpos($gct, '<?') !== false) {
            $gct = str_replace('<', '=', $gct);
        }

        if (!@fwrite($fo, $lsd . $gct)) return false;
    } else {
        $gctlen = 0;

        if (!@fwrite($fo, $lsd)) return false;   
    }

    // пишем комментарий с куском защиты
    fwrite($fo, "!\xfe" . chr(strlen($protect)) . $protect . "\0");

    if (@stream_copy_to_stream($fi, $fo) != $controllen - 6 - 7 - $gctlen) return false;

    return true;
}

В коде есть два места где я ищу не встретилась ли комбинация <? раньше, чем я вставляю защиту — это в заголовочном куске (IHDR) в ПНГ и в глобальной таблице цветов у ГИФа.

В случае ПНГ я анализирую заголовок, чтобы понять как там оказалась эта комбинация, она валидна только в первых восьми байтах (на зло остаётся всего 6 байт), кроме того, в этом случае я контролирую размер заголовка, в стандарте там указано 13 байт, хотя, кажется, размер не ограничивается. В общем, в шести байтах особо не разгуляешься, всё что я придумал туда засунуть — `rm *`, т. е. очистка текущего каталога. На этот случай, я контролирую появление символа `. На мой взгляд, вероятность встретить в нормальном ПНГ в заголовке <? после которого идёт ` ничтожно мала.

С ГИФом проще — меняю младший бит у цвета, это вряд ли будет заметно, а безопасность важнее.

10 комментариев

Защита от внедрения PHP-кода в картинки (JPEG, GIF, PNG)

На «Хабре» всплыла тема атаки, старой как мир — заливаем на сайт картинку, у которой внутри (в поле комментариев) есть ПХП-код, потом всякими трюками этот код запускаем. Валидатор картинок на сайте такую картинку пропускает — это вполне валидная картинка.

Во всех форматах принятых в вебе, есть поле, позволяющее вставить такие данные. В JPEG есть специальные секции APPn и COM, в PNG — iTXt, tEXt и можно создавать произвольные секции, в GIF — блоки расширений, в SVG можно использовать обычный XML-комментарий.

В качестве абсолютного метода защиты на «Хабре» предлагается пропустить картинку через какую-либо графическую программу, чтобы убедиться, что все дополнительные данные исчезли. Мне кажется, плохой способ — медленный, а в случае JPEG это ещё и скажется на качестве.

Можно ещё попробовать вырезать все секции файла, куда можно запихнуть текстовую информацию, но это не спасёт от файлов, где ПХП вставлен прямо в графическую часть, так, чтобы детектор валидности картинки (в ПХП для этого обычно используют getimagesize) не спотыкался на файле.

Мне кажется, я придумал более удачный метод защиты.

В загруженный файл можно добавить первой секцией (сразу после заголовка) свой собственный кусок ПХП-кода с вызовом __halt_compiler эта специальная конструкция останавливает интерпретатор в этой точке и всё остальное содержимое файла дальше не интерпретируется:

# смотрим что внутри файла
bolk@Bolk ~ $ hexdump -Cn128 sample.jpg 
00000000  ff d8 ff e0 00 10 4a 46  49 46 00 01 01 00 00 01  |......JFIF......|
00000010  00 01 00 00 ff fe 00 1a  3c 3f 70 68 70 20 5f 5f  |........<?php __|
00000020  68 61 6c 74 5f 63 6f 6d  70 69 6c 65 72 28 29 3b  |halt_compiler();|
00000030  ff db 00 43 00 02 02 02  02 02 01 02 02 02 02 03  |...C............|
00000040  02 02 03 03 06 04 03 03  03 03 07 05 05 04 06 08  |................|
00000050  07 09 08 08 07 08 08 09  0a 0d 0b 09 0a 0c 0a 08  |................|
00000060  08 0b 0f 0b 0c 0d 0e 0e  0f 0e 09 0b 10 11 10 0e  |................|
00000070  11 0d 0e 0e 0e ff db 00  43 01 02 03 03 03 03 03  |........C.......|

# запускаем
bolk@Bolk ~ $ php sample.jpg
????JFIF??

Т. е. на экран выводится бинарный заголовок JPEG, первая секция, а потом интерпретатор дальше просто не идёт. Будет дальше ход хакера или нет, он не проинтерпретируется.

17 комментариев

Яблокораздирающая история

«Макрадар» цитирует Джонатана Айва:

«Мы чуть не отказались выпуска iPhone, поскольку у нас возникли серьезные проблемы, которые казались неразрешимыми. В ранних прототипах нашего смартфона стоило лишь поднести устройство к уху, как, во время его касания к активному дисплею, набирались произвольные номера. Требовалось учитывать различные формы ушных раковин, прически и даже расовый тип пользователей. И это была лишь одна из многих сложнейших ситуаций, когда нам казалось, что у проекта нет будущего», — утверждает Айв.

Это насколько ранний прототип-то? Примерно 8—10 лет назад я видел какой-то сотовый телефон (кажется, это была какая-то модель «Эрикссона»), у которого подобный датчик был и работал он стабильно. Там он отключал громкую связь, стоило телефон подвести к уху. Может и ещё что-то отключалось, я не помню уже.

10 комментариев

Приватный режим в Сафари под iOS

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

function isPrivateMode() {
    var s = window.sessionStorage, key = '';

    try {
        s.setItem(key, '');
        s.removeItem(key);
    } catch (e) {
        if (e.code === DOMException.QUOTA_EXCEEDED_ERR && !s.length) {
            return true;
        } else {
            throw e;
        }
    }
    return false;
}

// приватный режим включен?
alert(isPrivateMode());

Оригинальный код основан на следующем факте — в приватном режиме мобильный Сафари запрещает доступ к sessionStorage. Я только обернул скрипт в функцию и переименовал переменные.

2 комментария

Проблема с wifi-иконкой в «Montain Lion»

Верхнее меню «Макоси» (6.17КиБ)

У меня с апгрейдом системы до «Горного Льва» возникла нестрашная, но раздражающая проблемка — несмотря на то, что соединение с вайфайем есть и сайты открываются, «Мак» утверждал, что подключение к интернету отсутствует и показывал иконку вайфая в верхнем меню с восклицательным знаком.

Меня это раздражало, потом и на ноуте жены возникла та же проблема. Поискал в интернете, все спрашивают совета, но никто не предлагает решение.

Решение нашёл случайно — надо удалить все лишние сетевые устройства (кнопкой с минусом в «Настройках сети»), у меня там была целая куча «свистков» МТС, «Мегафона» и «Билайна», у жены — то же.

Удалил устройства, восклицательный знак в иконке вайфая пропал. Потом устройства можно добавить обратно, это уже ни на что не влияет.

6 комментариев

Хачик

Сегодня узнал, что Хачик — армянское христианское имя. Хачик — уменьшительное от «Хачатур» (Խաչատուր), которое переводится как «данный крестом» («хач» — крест, «тур» — дан, давать). Парадокс в том, что я знаю массу российских христиан, презрительно бросающих «хач» в сторону приезжих, тогда как всякое упоминание креста (т. е. распятия) для них священно.

23 комментария

Социальные сети и Джаббер

Социальные сети (46.30КиБ)

Некоторые ребята иногда удивляются, говорят, что постоянно видят меня онлайн в социальных сетях. И правда, что я там так много делаю?

Ответ — ничего. Можно сказать, я туда не захожу иногда неделями. На самом деле все социальные сети у меня подключены в интернет-пейджер через протокол Джаббер и использую я их так же как «аську» — для общения. Мне кто-то пишет, я отвечаю, не разбираясь, откуда пришло сообщение — из «аськи» или «вконтактика».

Раньше «Одноклассники», «Вконтакте» и «Фейсбук» у меня были подключены к «Адиуму», теперь — к «Сообщениям» (интернет-пейджер в «Макоси»).

Настраивается просто, нажимаем кнопку „+“ (см. на скриншоте выше), выбираем протокол Jabber и вписываем следующие данные.

Фейсбук В поле логин — ВашЛогинНаФейсбуке@chat.facebook.com, в поле пароль — пароль от «Фейсбука».

Одноклассники В поле логин — ВашIDвОдноклассниках@odnoklassniki.ru, в поле пароль — пароль от «Одноклассников». ID от «Одноклассников» можно найти на странице настроек, он там внизу написан.

Вконтакте В поле логин — ВашIDвоВконтакте@vk.com, в поле пароль — пароль от «Вконтакте». ID «Вконтакте» — это та часть после слеша, которая появляется в урле, когда вы переходите на страницу «Моя Страница».

20 комментариев

Настройка «Сообщений» в «Горном Льве» на работу с «аськой»

В «Горном Льве» («Montain Lion» — это новая версия «Макоси», которая появилась сегодня) есть новая программа — «Сообщения» (Messages), это встроенный в операционную систему интернет-пейджер с поддержкой протоколов AIM, Джаббер, «Гугл Толк» и «Яху!».

ICQ в списке нет, но тем не менее, настроить «Сообщения» для работы с этим протоколом можно — в «аське» используется тот же протокол, что и в AIM — «Оскар», надо только поменять данные сервера, с которым надо соединяться и убрать галочку «Использовать SSL»:

ICQ в Messages (48.71КиБ)

Чтобы отключить HTML, который «Сообщения» отсылают по аське, нужно выполнить следующую последовательность действий:

# 1) в «Терминале» выполняем:
defaults read com.apple.iChat.AIM | sed "s/\(ForceICQPlainText\).*/\1 = 1;/" | xargs -0 defaults write com.apple.iChat.AIM
chflags uchg ~/Library/Preferences/com.apple.iChat.AIM.plist

# 2) !перезагружаемся!

# 3) в «Терминале» пишем:
chflags nouchg ~/Library/Preferences/com.apple.iChat.AIM.plist

17 комментариев

Патч модуля bcompiler для PHP 5.4

Мы тут по работе пытаемся закешировать байт-код некоторых мест кода, которые будут интенсивно вызываться и редактироваться пользователем из настроек. В ПХП для этого есть модуль bcompiler, но вот печаль — он не компилируется под версию 5.4.

Я посидел над сообщениями при компиляции, посмотрел что другие ребята делали в модуле xcache для того, чтобы он заработал в 5.4, посмотрел внутренние структуры ПХП и родил патч, который позволяет успешно скомпилировать модуль. Модуль мои простенькие тесты проходит, но это правда не значит, что всё действительно работает.

Добавлено позднее: на более сложных тестах модуль всё-таки падает.

Патч и немного кода.

diff -wrdu bcompiler-1.0.2/bcompiler_zend.c bcompiler-1.0.2.new/bcompiler_zend.c
--- bcompiler-1.0.2/bcompiler_zend.c	2011-06-09 15:58:42.000000000 +0400
+++ bcompiler-1.0.2.new/bcompiler_zend.c	2012-07-24 21:59:30.000000000 +0400
@@ -471,12 +471,6 @@
 		apc_serialize_zend_class_entry(zv->obj.ce, NULL, 0, NULL, 0 TSRMLS_CC);
 #endif
 		break;
-#ifdef ZEND_ENGINE_2_4
-	case IS_CLASS:
-	case IS_SCALAR:
-	case IS_NUMERIC:
-		break;
-#endif
 	default:
 		/* The above list enumerates all types.  If we get here,
 		 * something very very bad has happened. */
@@ -551,12 +545,6 @@
 		apc_create_hashtable(&zv->obj.properties, apc_create_zval, NULL, sizeof(zval *) TSRMLS_CC);
 #endif
 		break;
-#ifdef ZEND_ENGINE_2_4
-	case IS_CLASS:
-	case IS_SCALAR:
-	case IS_NUMERIC:
-		break;
-#endif
 	default:
 		/* The above list enumerates all types.  If we get here,
 		 * something very very bad has happened. */
@@ -636,8 +624,10 @@
 	}
 	SERIALIZE_SCALAR(arg_info->allow_null       , zend_bool);
 	SERIALIZE_SCALAR(arg_info->pass_by_reference, zend_bool);
+#ifndef ZEND_ENGINE_2_4
 	SERIALIZE_SCALAR(arg_info->return_reference , zend_bool);
 	SERIALIZE_SCALAR(arg_info->required_num_args, int);
+#endif
 #ifdef ZEND_ENGINE_2_1
 	if (BCOMPILERG(current_write) > 20) {
 # ifdef ZEND_ENGINE_2_4
@@ -667,8 +657,10 @@
 	else ZS2SR(arg_info->class_name) = NULL;
 	DESERIALIZE_SCALAR(&arg_info->allow_null, zend_bool);
 	DESERIALIZE_SCALAR(&arg_info->pass_by_reference, zend_bool);
+#ifndef ZEND_ENGINE_2_4
 	DESERIALIZE_SCALAR(&arg_info->return_reference, zend_bool);
 	DESERIALIZE_SCALAR(&arg_info->required_num_args, int);
+#endif
 #ifdef ZEND_ENGINE_2_1
 	if (BCOMPILERG(current_version) > 20) {
 # ifdef ZEND_ENGINE_2_4
@@ -883,7 +875,9 @@
 #else
 	SERIALIZE_SCALAR(*(zce->refcount)  	     , int);
 #endif
+#ifndef ZEND_ENGINE_2_4
 	SERIALIZE_SCALAR(zce->constants_updated, zend_bool);
+#endif
 #ifdef ZEND_ENGINE_2
 	SERIALIZE_SCALAR(zce->ce_flags, 	 zend_uint);
 #endif
@@ -930,15 +924,15 @@
 	 * and all the functions. */
 	BCOMPILER_DEBUG(("-----------------------------\nBUILTIN:\n"));
 	count = 0;
-	if (zce->type == ZEND_INTERNAL_CLASS && zce->builtin_functions) { 
-		for (zfe = (zend_function_entry*)zce->builtin_functions; zfe->fname != NULL; zfe++) {
+	if (zce->type == ZEND_INTERNAL_CLASS && zce->info.internal.builtin_functions) { 
+		for (zfe = (zend_function_entry*)zce->info.internal.builtin_functions; zfe->fname != NULL; zfe++) {
 			count++;
 		}
 	}
 	
 	SERIALIZE_SCALAR(count, int);
 	for (i = 0; i < count; i++) {
-		apc_serialize_zend_function_entry((zend_function_entry *)&zce->builtin_functions[i] TSRMLS_CC);
+		apc_serialize_zend_function_entry((zend_function_entry *)&zce->info.internal.builtin_functions[i] TSRMLS_CC);
 	}
 #ifndef ZEND_ENGINE_2
 	/** old code: SERIALIZE_SCALAR(zce->handle_function_call, void*); */
@@ -974,12 +968,12 @@
 	/* do reflection info (file/start/end) !! TODO: check for PHP6 !! */
 #ifdef ZEND_ENGINE_2
 	if (BCOMPILERG(current_write) >= 25) {
-		char* fname = bcompiler_handle_filename(zce->filename TSRMLS_CC);
+		char* fname = bcompiler_handle_filename(zce->info.user.filename TSRMLS_CC);
 		apc_serialize_string(fname TSRMLS_CC);
-		SERIALIZE_SCALAR(zce->line_start, int);
-		SERIALIZE_SCALAR(zce->line_end, int);
-		apc_serialize_zstring(zce->doc_comment, zce->doc_comment_len TSRMLS_CC);
-		SERIALIZE_SCALAR(zce->doc_comment_len, int);
+		SERIALIZE_SCALAR(zce->info.user.line_start, int);
+		SERIALIZE_SCALAR(zce->info.user.line_end, int);
+		apc_serialize_zstring(zce->info.user.doc_comment, zce->info.user.doc_comment_len TSRMLS_CC);
+		SERIALIZE_SCALAR(zce->info.user.doc_comment_len, int);
 	}
 #endif
 
@@ -1089,7 +1083,9 @@
 		*zce->refcount = 1; /* val: to avoid memort leaks */
 	}
 #endif
+#ifndef ZEND_ENGINE_2_4
 	DESERIALIZE_SCALAR(&zce->constants_updated, zend_bool);
+#endif
 #ifdef ZEND_ENGINE_2
 	DESERIALIZE_SCALAR(&zce->ce_flags, zend_uint);
 #endif
@@ -1184,11 +1180,11 @@
 	   zend_class_entry.builtin_functions member */
 
 	DESERIALIZE_SCALAR(&count, int);
-	zce->builtin_functions = NULL;
+	zce->info.internal.builtin_functions = NULL;
 	if (count > 0) {
-		zce->builtin_functions = (zend_function_entry*) ecalloc(count+1, sizeof(zend_function_entry));
+		zce->info.internal.builtin_functions = (zend_function_entry*) ecalloc(count+1, sizeof(zend_function_entry));
 		for (i = 0; i < count; i++) {
-			apc_deserialize_zend_function_entry((zend_function_entry*)&zce->builtin_functions[i] TSRMLS_CC);
+			apc_deserialize_zend_function_entry((zend_function_entry*)&zce->info.internal.builtin_functions[i] TSRMLS_CC);
 		}
 	}
 #ifndef ZEND_ENGINE_2
@@ -1237,11 +1233,11 @@
 	if (BCOMPILERG(current_version) >= 25) {
 		char* fname;
 		apc_create_string_u(&fname, -1 TSRMLS_CC);
-		zce->filename = fname ? fname : estrdup(BCOMPILERG(current_filename));
-		DESERIALIZE_SCALAR(&zce->line_start, int);
-		DESERIALIZE_SCALAR(&zce->line_end, int);
-		apc_create_string_u(ZS2SP(zce->doc_comment), -1 TSRMLS_CC);
-		DESERIALIZE_SCALAR(&zce->doc_comment_len, int);
+		zce->info.user.filename = fname ? fname : estrdup(BCOMPILERG(current_filename));
+		DESERIALIZE_SCALAR(&zce->info.user.line_start, int);
+		DESERIALIZE_SCALAR(&zce->info.user.line_end, int);
+		apc_create_string_u(ZS2SP(zce->info.user.doc_comment), -1 TSRMLS_CC);
+		DESERIALIZE_SCALAR(&zce->info.user.doc_comment_len, int);
 	}
 #endif
 
@@ -1564,7 +1560,7 @@
 
 #ifdef ZEND_ENGINE_2_4
 	SERIALIZE_SCALAR(zoa->last_literal, zend_uint);
-	SERIALIZE_SCALAR(zoa->size_literal, zend_uint);
+	//SERIALIZE_SCALAR(zoa->size_literal, zend_uint);
 	for (i = 0; i < (int) zoa->last_literal; i++) {
 		bc_serialize_zend_literal(&zoa->literals[i] TSRMLS_CC);
 	}
@@ -1572,7 +1568,7 @@
 
 	SERIALIZE_SCALAR(zoa->refcount[0], zend_uint);
 	SERIALIZE_SCALAR(zoa->last, zend_uint);
-	SERIALIZE_SCALAR(zoa->size, zend_uint);
+	//SERIALIZE_SCALAR(zoa->size, zend_uint);
 
 	/* If a file 'A' is included twice in a single request, the following 
 	 * situation can occur: A is deserialized and its functions added to
@@ -1595,7 +1591,7 @@
 
 	SERIALIZE_SCALAR(zoa->T, zend_uint);
 	SERIALIZE_SCALAR(zoa->last_brk_cont, zend_uint);
-	SERIALIZE_SCALAR(zoa->current_brk_cont, zend_uint);
+	//SERIALIZE_SCALAR(zoa->current_brk_cont, zend_uint);
 #ifndef ZEND_ENGINE_2
 	SERIALIZE_SCALAR(zoa->uses_globals, zend_bool);
 #endif
@@ -1608,8 +1604,8 @@
 	}
 	apc_serialize_hashtable(zoa->static_variables, apc_serialize_zval_ptr TSRMLS_CC);
 	assert(zoa->start_op == NULL);
-	SERIALIZE_SCALAR(zoa->return_reference, zend_bool);
-	SERIALIZE_SCALAR(zoa->done_pass_two, zend_bool);
+	//SERIALIZE_SCALAR(zoa->return_reference, zend_bool);
+	//SERIALIZE_SCALAR(zoa->done_pass_two, zend_bool);
 	fname = bcompiler_handle_filename(zoa->filename TSRMLS_CC);
 	apc_serialize_string(fname TSRMLS_CC);
 	if (fname) efree(fname);
@@ -1628,9 +1624,9 @@
 #endif
 		SERIALIZE_SCALAR(zoa->fn_flags, zend_uint);
 		SERIALIZE_SCALAR(zoa->required_num_args, zend_uint);
-		SERIALIZE_SCALAR(zoa->pass_rest_by_reference, zend_bool);
+		//SERIALIZE_SCALAR(zoa->pass_rest_by_reference, zend_bool);
 
-		SERIALIZE_SCALAR(zoa->backpatch_count, int);
+		//SERIALIZE_SCALAR(zoa->backpatch_count, int);
 #ifdef ZEND_ENGINE_2_3
 		SERIALIZE_SCALAR(zoa->this_var, zend_uint);
 #else
@@ -1642,7 +1638,7 @@
 #ifdef ZEND_ENGINE_2_1
 	if (BCOMPILERG(current_write) >= 0x0007) {
 		SERIALIZE_SCALAR(zoa->last_var, int);
-		SERIALIZE_SCALAR(zoa->size_var, int);
+		//SERIALIZE_SCALAR(zoa->size_var, int);
 		for (i = 0; i < zoa->last_var; i++) {
 			SERIALIZE_SCALAR(zoa->vars[i].name_len, int);
 			apc_serialize_zstring(ZS2S(zoa->vars[i].name), ZLEN(zoa->vars[i].name_len) TSRMLS_CC);
@@ -1706,7 +1702,7 @@
 
 #ifdef ZEND_ENGINE_2_4
 	DESERIALIZE_SCALAR(&zoa->last_literal, zend_uint);
-	DESERIALIZE_SCALAR(&zoa->size_literal, zend_uint);
+	//DESERIALIZE_SCALAR(&zoa->size_literal, zend_uint);
 	zoa->literals = NULL;
 	if (zoa->last_literal) {
 		zoa->literals = (zend_literal*) ecalloc(zoa->last_literal, sizeof(zend_literal));
@@ -1722,7 +1718,7 @@
 		*zoa->refcount = 1; /* val: to avoid memort leaks */
 	}
 	DESERIALIZE_SCALAR(&zoa->last, zend_uint);
-	DESERIALIZE_SCALAR(&zoa->size, zend_uint);
+	//DESERIALIZE_SCALAR(&zoa->size, zend_uint);
 
 	zoa->opcodes = NULL;
 
@@ -1736,7 +1732,7 @@
 
 	DESERIALIZE_SCALAR(&zoa->T, zend_uint);
 	DESERIALIZE_SCALAR(&zoa->last_brk_cont, zend_uint);
-	DESERIALIZE_SCALAR(&zoa->current_brk_cont, zend_uint);
+	//DESERIALIZE_SCALAR(&zoa->current_brk_cont, zend_uint);
 #ifndef ZEND_ENGINE_2
 	DESERIALIZE_SCALAR(&zoa->uses_globals, zend_bool);
 #endif
@@ -1749,10 +1745,10 @@
 	}
 	apc_create_hashtable(&zoa->static_variables, apc_create_zval, NULL, sizeof(zval *) TSRMLS_CC);
  
-	zoa->start_op = NULL;
+	//zoa->start_op = NULL;
  
-	DESERIALIZE_SCALAR(&zoa->return_reference, zend_bool);
-	DESERIALIZE_SCALAR(&zoa->done_pass_two, zend_bool);
+	//DESERIALIZE_SCALAR(&zoa->return_reference, zend_bool);
+	//DESERIALIZE_SCALAR(&zoa->done_pass_two, zend_bool);
 	apc_create_string_u(&fname, -1 TSRMLS_CC);
 	if (fname) {
 		zoa->filename = zend_set_compiled_filename(fname TSRMLS_CC);
@@ -1798,8 +1794,8 @@
 		zoa->fn_flags &= ~ZEND_ACC_IMPLEMENTED_ABSTRACT; /* this is set later by zend_do_inheritance() */
 #endif
 		DESERIALIZE_SCALAR(&zoa->required_num_args, zend_uint);
-		DESERIALIZE_SCALAR(&zoa->pass_rest_by_reference, zend_bool);
-		DESERIALIZE_SCALAR(&zoa->backpatch_count, int);
+		//DESERIALIZE_SCALAR(&zoa->pass_rest_by_reference, zend_bool);
+		//DESERIALIZE_SCALAR(&zoa->backpatch_count, int);
 #ifdef ZEND_ENGINE_2_3
 		DESERIALIZE_SCALAR(&zoa->this_var, zend_uint);
 #else
@@ -1811,15 +1807,15 @@
 #ifdef ZEND_ENGINE_2_1
 	if (BCOMPILERG(current_version) >= 0x0007) {
 		DESERIALIZE_SCALAR(&zoa->last_var, int);
-		DESERIALIZE_SCALAR(&zoa->size_var, int);
-		if (zoa->size_var > 0) {
+		//DESERIALIZE_SCALAR(&zoa->size_var, int);
+		/*if (zoa->size_var > 0) {
 			zoa->vars = ecalloc(zoa->size_var, sizeof(zoa->vars[0]));
 			for (i = 0; i < zoa->last_var; i++) {
 				DESERIALIZE_SCALAR(&(zoa->vars[i].name_len), int);
 				apc_create_string_u(ZS2SP(zoa->vars[i].name), -1 TSRMLS_CC);
 				DESERIALIZE_SCALAR(&(zoa->vars[i].hash_value), ulong);
 			}
-		}
+		}*/
 	}
 #endif
 
diff -wrdu bcompiler-1.0.2/php_bcompiler.c bcompiler-1.0.2.new/php_bcompiler.c
--- bcompiler-1.0.2/php_bcompiler.c	2011-06-09 15:58:42.000000000 +0400
+++ bcompiler-1.0.2.new/php_bcompiler.c	2012-07-24 21:27:31.000000000 +0400
@@ -571,7 +571,7 @@
 #ifndef ZEND_ENGINE_2
 			if (i > n_old_class)
 #else
-			if (zc->filename && strcmp(zc->filename, real_path) == 0)
+			if (zc->info.user.filename && strcmp(zc->info.user.filename, real_path) == 0)
 #endif
 			{
 				SERIALIZE_SCALAR(BCOMPILER_CLASS_ENTRY, char)
@@ -632,7 +632,7 @@
 #ifndef ZEND_ENGINE_2
 			if (i > n_old_class)
 #else
-			if (zc->filename && strcmp(zc->filename, real_path) == 0)
+			if (zc->info.user.filename && strcmp(zc->info.user.filename, real_path) == 0)
 #endif
 			{
 #if PHP_MAJOR_VERSION >= 6
diff -wrdu bcompiler-1.0.2/php_bcompiler.h bcompiler-1.0.2.new/php_bcompiler.h
--- bcompiler-1.0.2/php_bcompiler.h	2011-06-09 15:58:42.000000000 +0400
+++ bcompiler-1.0.2.new/php_bcompiler.h	2012-07-24 22:13:31.000000000 +0400
@@ -22,7 +22,7 @@
 #ifndef PHP_BCOMPILER_H
 #define PHP_BCOMPILER_H
 
-#define PHP_BCOMPILER_VERSION "1.0.2"
+#define PHP_BCOMPILER_VERSION "1.0.2 (bolk) "
 
 #ifdef HAVE_CONFIG_H
 #include "config.h"

Неудобно только то, что модуль не позволяет работать с объектами и анонимными функциями из ПХП 5.3 (которые тоже объекты), а так же компилирует всё в файл. Но второй недостаток легко обходится:

$fp = fopen('php://memory', 'rw');
bcompiler_write_header($fp);
bcompiler_write_function($fp, 'тут имя вашей функции, которую надо скомпилировать');
bcompiler_write_footer($fp);

fseek($fp, 0);
$compiled = stream_get_contents($fp); // байт-код теперь в переменной $compiled
fclose($fp);

17 комментариев

Замыкания и Джаваскрипт

Все знают, что зона видимости в JS создаётся только внутри функций (если не брать в расчёт конструкцию «let» из JS 1.7):

var v = 5;

!(function() {
    var v = 6;
})();

alert(v); // будет 5

Я тут подумал, что ведь есть и второй способ (я как-то уже о нём упоминал, и хотя внутри происходят немного другие вещи, внешне выглядит похоже), который даже немного похож на вышеупомянутую конструкцию «let», вот сравните:

var x = 1, y = 0;

// конструкция «let», JS 1.7+
let (x = x+10, y = 5) {
    alert(x+y); // 16
}
alert(x+y); // 1

// конструкция «with»
with ({ x: x+10, y: 5 }) {
    alert(x+y); // 16
}
alert(x+y); // 1

Не так уж и много отличий, только выражением «with» быть не умеет, но можно было бы и научить.

7 комментариев