Опять PHP — скрытая подмена понятий :)

<?
print_r($_POST);
?>
<form method=»POST»>
<input name=»через пробел»>
<input type=»submit»>
</form>

Как вы думаете, что выведет print_r? Array ( [через пробел] => ) ? А вот и нифига!

Он выведет Array ( [через_пробел] => )

Вот такая зашитая фича. Для пробелов и точек.

Берется отсюда:

В файле main/php_variables.c, php_register_variable_ex
(92 строка для PHP 5.1.4):

/* ensure that we don’t have spaces or dots in the variable name (not binary
safe) */
for (p = var; *p; p++) {
if (*p == ‘ ‘ || *p == ‘.’) {
*p=’_’;
} else if (*p == ‘[‘) {
is_array = 1;
ip = p;
*p = 0;
break;
}
}

var_len = p — var;

Почему? Зачем? Не нашёл. Факт остаётся фактом. Нот бинари сейф, йоптыж.

Кодировка по умолчанию — php много на себя берёт

Весь мозг себе сьел.

Перенёс сайт на сервер. На предыдущем хостинге было всё ок — открывался как надо, все дела.

На новой же системе — постоянный header UTF-8, хоть ты тресни. Я бы и рад конечно UTF — но HostCMS пока что поддерживает только Win1251 — так что стал копать.

Перерыл весь апач — AddDefaultCharset не помогает.
.htaccess — эти параметры вообще как будто не читаются.
БД — всё правильно стоит, SET NAMES CP1251

Грыз ногти минут 20, перекрывающие header() в скриптах не работают — часть сайта стала нормальной, часть — нет.

И бац, вуаля, рекурсивный find находит мне строку:
root@evilman:/etc/php5/apache2# cat php.ini |grep utf
default_charset = «utf8»

#*$&#*$& — всё проклял, ейбгу. ПХП насаживал дефолтный UTF8, игнорируя все директивы Апача.

Вот так-то

Новый логин Вконтакте — Perl, Curl

Собственно, уже не мудрствуя лукаво — кто хоть немного знает перл — разберётся.

Вконтакте добавили редиректики, что не помешает доблестному Curl через них пройти и получить куку в файл.

#!/usr/bin/perl -w
use strict;
use WWW::Curl::Easy;
use Carp;

sub getURL {
  my $params = shift;

  croak q[HASH needed as param] if ref($params) ne ‘HASH’;
  croak q[HREF needed in HASH] if(!exists($params->{href}));

  $params->{href} = q[http://].$params->{href} if $params->{href} !~ /^http(s)?:\/\//;
  
  my $result = ‘false’;
  open my $oldout, «>&STDOUT» or die «Can’t dup STDOUT: $!»;

  close STDOUT;
  open STDOUT, «> /dev/null»;
  my $retcode;

  my $curl;
  {
    $curl = new WWW::Curl::Easy;
    $curl->setopt(CURLOPT_URL, $params->{href});
    $curl->setopt(CURLOPT_USERAGENT,q[q[Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.2) Gecko/20100115 Firefox/3.6]]);
    $curl->setopt(CURLOPT_POST,1) if $params->{post};
    $curl->setopt(CURLOPT_POSTFIELDS,$params->{post}) if $params->{post};
    $curl->setopt(CURLOPT_COOKIEJAR, $params->{cookie_file}) if $params->{cookie_file};
    $curl->setopt(CURLOPT_COOKIEFILE, $params->{cookie_file}) if $params->{cookie_file};
    $curl->setopt(CURLOPT_FOLLOWLOCATION,1);
    open (my $tmp_for_curl, «>», \$result);
    $curl->setopt(CURLOPT_FILE,$tmp_for_curl);
    $retcode = $curl->perform;
  }

  close STDOUT;
  open STDOUT, «>&», $oldout or die «Can’t dup \$oldout: $!»;

  return {
        content         => $result,
        retcode         => $retcode,
        http_code        => $curl->getinfo(CURLINFO_HTTP_CODE),
        effective_url      => $curl->getinfo(CURLINFO_EFFECTIVE_URL),
        content_type      => $curl->getinfo(CURLINFO_CONTENT_TYPE)
      };
}

my $login     =  q[your@email];
my $password    =  q[password];
my $cookie_file  =  q[./cookie_file]
$login       =~  s/@/%40/;
my $result     =  getURL({
                href => q[http://vkontakte.ru/login.php],
                post => qq[op=>a_login_attempt&email=$login&pass=$password&expire=0],
                cookie_file => $cookie_file
            });

Любимая функция для получения контента

Таскаю за собой по всем модулям и скриптам.

sub getURL {
  my $params = shift;

  croak q[HASH needed as param] if ref($params) ne ‘HASH’;

  croak q[HREF needed in HASH] if(!exists($params->{href}));

  $params->{href} = q[http://].$params->{href} if $params->{href} !~ /^http(s)?:\/\//;
  $params->{headers} = 0 if !$params->{headers};
  $params->{ag} = qq($user_agents[rand(($#user_agents+1))]) if !exists $params->{ag};
  $params->{timeout} = 20 if !exists $params->{timeout};
  $params->{content_length} = 600000000 if !exists $params->{content_length};

  my $result = ‘false’;
  open my $oldout, «>&STDOUT» or die «Can’t dup STDOUT: $!»;

  close STDOUT;
  open STDOUT, «> /dev/null»;
  my $retcode;

  my $curl;
  {
    $curl = new WWW::Curl::Easy;

    $curl->setopt(CURLOPT_URL, $params->{href});
    $curl->setopt(CURLOPT_INTERFACE, $params->{ip}) if exists $params->{ip};
    $curl->setopt(CURLOPT_CONNECTTIMEOUT,$params->{timeout});
    $curl->setopt(CURLOPT_TIMEOUT,$params->{timeout});
    $curl->setopt(CURLOPT_NOPROGRESS, 0);
    $curl->setopt(CURLOPT_PROGRESSFUNCTION, sub {$_[2]>$params->{content_length}?1:0});
    $curl->setopt(CURLOPT_USERAGENT,$params->{ag});
    $curl->setopt(CURLOPT_POST,1) if $params->{post};
    $curl->setopt(CURLOPT_PROXY,$params->{proxy}) if $params->{proxy};
    $curl->setopt(CURLOPT_POSTFIELDS,$params->{post}) if $params->{post};
    $curl->setopt(CURLOPT_HEADER,$params->{headers});
    $curl->setopt(CURLOPT_COOKIE,$params->{cookie_string}) if $params->{cookie_string};
    $curl->setopt(CURLOPT_COOKIEJAR, $params->{cookie_file}) if $params->{cookie_file};
    $curl->setopt(CURLOPT_COOKIEFILE, $params->{cookie_file}) if $params->{cookie_file};
    $curl->setopt(CURLOPT_FOLLOWLOCATION,1);
    $curl->setopt(CURLOPT_REFERER,$params->{referer}) if exists $params->{referer};
    open (my $tmp_for_curl, «>», \$result);
    $curl->setopt(CURLOPT_FILE,$tmp_for_curl);
    $retcode = $curl->perform;
  }

  close STDOUT;
  open STDOUT, «>&», $oldout or die «Can’t dup \$oldout: $!»;

  return {
        content         => $result,
        retcode         => $retcode,
        http_code        => $curl->getinfo(CURLINFO_HTTP_CODE),
        effective_url      => $curl->getinfo(CURLINFO_EFFECTIVE_URL),
        content_type      => $curl->getinfo(CURLINFO_CONTENT_TYPE)
      };
}

«Защита» от обновления страницы по F5 :)

Навеяно SE и паранойей тамошнего ТС к ддос через F5 :DDD


<script type="text/javascript">
document.onkeydown = KeyCheck;
function KeyCheck(e) {
  var KeyID = (window.event) ? event.keyCode : e.keyCode;
  if(KeyID == 116){
    return false;
  }
}
</script>

Чем отличается PHP от FTP?

Этот вопрос мучает людей веками.

Ответ на вопрос «Чем отличается PHP от FTP» прост. Но его никто не знает.

Мерзкие отличия MacOS от Linux

Задача: массово переконвертить файлы с расширением .php из Windows-1251 в UTF-8.

Итоговое решение:

for a in `find /files_folder -type f -name "*.php"`; do iconv -f Windows-1251 -t UTF8 $a > /tmp/cp.file; cat /tmp/cp.file > $a; done; rm -rf /tmp/cp.file

Ну не ппц ли. Тамошний xargs не поддерживает -i, -I и -J работают через пень-колоду, а у iconv нет параметра -o :(

Создать модуль для CPAN

Скорее для себя, чтобы не забыть

$ cd /path/to/dev/folder/
$ h2xs -b 5.8.8 -AX Foo::Bar -v 1.00
Writing Foo-Bar/lib/Foo/Bar.pm
Writing Foo-Bar/Makefile.PL
Writing Foo-Bar/README
Writing Foo-Bar/t/Foo-Bar.t
Writing Foo-Bar/Changes
Writing Foo-Bar/MANIFEST

-b — минимальная версия Perl, нужная для работы модуля
-v — версия модуля

После чего распихиваем код по соответствующим файлам, не забывая про PerlDoc и тесты, пробуем собрать

$ cd Foo-Bar
$ perl Makefile.PL
$ make
$ make test
$ make clean

Ошибок быть не должно — если есть — исправьте.)

Проверяем как выглядит Pod

$ pod2html lib/Foo/Bar.pm > Bar.htm

Сгенерит вам то, что будет видеть CPAN в вашем поде

Убеждаемся что всё в порядке и

$ tar cvf Foo-Bar-1.00.tar Foo-Bar-1.00
$ gzip —best Foo-Bar-1.00.tar

Для того, чтобы залить модуль на CPAN — надо иметь аккаунт :)

http://pause.perl.org — заявка у меня лично рассмотрелась за сутки. Коллег рассматривали неделю :)

После рассмотрения вам приходит письмо счастья — и в личном кабинете через Upload загружаете ваш архивчик.

У меня появился через день — уже на CPAN. Может пройти до недели.

В общем-то всё просто :)

мой первый модуль на CPAN

IMAP и POP Gmail через telnet или читать почту это просто

В этом нет ничего сложного — правда! Фактически — написать свой почтовик — проще простого. Так же просто как и читать почту через консоль.

Далее — две маленькие статьи на живых примерах — как пользоваться POP3 и IMAP без почтового клиента.

Читаем POP3

Вам потребуется Linux. Ну или поддержка OpenSSL в Windows — если она у вас есть — то, вероятно, статья не для вас — вы и так всё знаете :)

Далее — набор команд с расшифровкой:

openssl s_client -crlf -connect pop.gmail.com:995

Мы коннектимся на гмейл с использованием SSL. Параметр -crlf гарантирует что нам не придётся испытывать проблем с переносом строк и наши команды будут распознаны так, как надо.
Параметр -ign_eof обещает нам, что команды, начинающиеся с буквы R будут корректно восприняты s_client и не вызовут разрыва SSL.

В случае успешного соединения мы видим что-то типа следующего:

CONNECTED(00000003)
куча букв и цифр, свидетельствующих о SSL
+OK Gpop ready for requests from 79.165.189.32 3pf3718132bwz.16

Это значит что на первом этапе всё прошло отлично и мы присоединились к почтовику Гугла.
Далее нам следует авторизоваться.

USER логин БЕЗ @gmail.com

В случае успешного прохождения команды мы видим строчку:

+OK send PASS

Нас просят ввести пароль. Что мы и сделаем:

PASS ваш_пароль

В случае если пароль верен — нас пускают внутрь!

+OK Welcome.

Вот мы и внутри нашего почтового ящика.
Давайте проверим — пришли ли нам новые сообщения?

STAT

Я увидел следующее:

+OK 15 1408449

Это значит, что на сервере у меня 15 непрочитанных писем общим размером 1 408 449 байт.

Интересно — а сколько весит каждое непрочитанное письмо?

LIST

Вот и список сообщений:

+OK 15 messages (1408449 bytes)
1 3423
2 42610
3 3693
4 3693
5 445122
6 1933
7 3488
8 3760
9 3155
10 439325
11 9071
12 3125
13 3575
14 3997
15 438479

Видите — пока ничего сложного :)

Давайте прочитаем пятое сообщение — что-то в нём много байт — наверное, интересное письмо!

Для чтения письма доступна команда TOP

TOP 5 0

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


From: root
To: master@skazkin.ru
Subject: Cron perl /home/common/parser.pl queue

Мне пришёл отчёт с моего сервера — что парсер закончил строить очередь. Хорошо, читать мне это не очень интересно — там разная служебная информация.
Пометим его для удаления

DELE 5

Гмейл понял нас —

+OK marked for deletion

Гмейл удалит отмеченные для удаления письма после выхода из системы.

Прочитаем самое маленькое письмо —

TOP 6 0

Subject: =?KOI8-R?B?0NLJ18XU?=
From: =?KOI8-R?B?4c7Uz84g58HCz9c=?= <друг@gmail.com>
To: Andrew Skazkin <мне@gmail.com>
Content-Type: text/plain; charset=ISO-8859-1

Ого — не читается :(
Это уже решается почтовыми клиентами — тема письма — в кодировке KOI8-R и зашифрована BASE64.

Чтож, почта прочитана и пора выходить.

Я так подумал — не буду я удалять письмо от Cron.

RSET

Эта команда сбрасывает флажки на удаление у писем, которые мы отметили для удаления.

+OK

Такой ответ — в случае успеха. Теперь можно выйти.

QUIT

Ответом нам —

+OK Farewell.
read:errno=0

Вот и всё. С POP3 мы разобрались, не так ли? Пришла пора для IMAP.

Играемся с IMAP

Процедура мало чем отличается — только лишь командами.

openssl s_client -crlf -ign_eof -connect imap.gmail.com:993

Опять выдала нам кучу букв и цифр, и последняя строчка —

+OK Gpop ready for requests from 79.165.189.32 1pf4035116fxm.33

Входим в аккаунт:

. login я@gmail.com мойпароль

Успешно!

* CAPABILITY IMAP4rev1 UNSELECT LITERAL+ IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS COMPRESS=DEFLATE
. OK я@gmail.com authenticated (Success)

Обратите внимание — все команды предваряются точкой и пробелом!

Получим список наших папок в аккаунте:

. list «» «*»

Нам вываливается список папок:

* LIST (\HasNoChildren) «/» «Cron»
* LIST (\HasNoChildren) «/» «INBOX»
* LIST (\HasNoChildren) «/» «LJ»
* LIST (\HasNoChildren) «/» «Proxies»
* LIST (\HasNoChildren) «/» «WordPress»
* LIST (\Noselect \HasChildren) «/» «[Gmail]»
* LIST (\HasNoChildren) «/» «[Gmail]/&BBIEQQRP- &BD8EPgRHBEIEMA-»
* LIST (\HasNoChildren) «/» «[Gmail]/&BBoEPgRABDcEOAQ9BDA-»
* LIST (\HasNoChildren) «/» «[Gmail]/&BB4EQgQ,BEAEMAQyBDsENQQ9BD0ESwQ1-»
* LIST (\HasNoChildren) «/» «[Gmail]/&BB8EPgQ8BDUERwQ1BD0EPQRLBDU-»
* LIST (\HasNoChildren) «/» «[Gmail]/&BCEEPwQwBDw-»
* LIST (\HasNoChildren) «/» «[Gmail]/&BCcENQRABD0EPgQyBDgEOgQ4-»
. OK Success

То, что с непонятными символами — папки, названные по-русски.

Сколько у нас писем в папке INBOX?

. status INBOX (messages)

Ответ —

* STATUS «INBOX» (MESSAGES 8)
. OK Success

Ого! Есть новая почта! Почитаем её:

. select inbox

Выборка успешна:

* FLAGS (\Answered \Flagged \Draft \Deleted \Seen)
* OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen \*)]
* OK [UIDVALIDITY 2]
* 8 EXISTS
* 0 RECENT
* OK [UIDNEXT 68967]
. OK [READ-WRITE] inbox selected. (Success)

Посмотрим — что из восьми писем ещё не прочитано:

. fetch 1:* flags

Эта команда выбирает флаги для всех писем, начиная с первого.

* 1 FETCH (FLAGS (\Seen))
* 2 FETCH (FLAGS (\Seen))
* 3 FETCH (FLAGS (\Seen))
* 4 FETCH (FLAGS (\Seen))
* 5 FETCH (FLAGS (\Seen))
* 6 FETCH (FLAGS ())
* 7 FETCH (FLAGS ())
* 8 FETCH (FLAGS ())
. OK Success

Прочитаем седьмое письмо — его заголовки

. fetch 7 full

* 7 FETCH (ENVELOPE («Fri, 20 Nov 2009 09:58:35 +0000» «=?KOI8-R?B?79TXxdTJ1NggzsEg08/Pwt3FzsnFICfSwcLP1MXOy8EgzsEgMSDCwcvTJw==?=» ((«=?KOI8-R?B?5s/S1c0gzyDQz8nTy8/X2cgg08nT1MXNwcg=?=» NIL «root» «searchengines.ru»)) ((«=?KOI8-R?B?5s/S1c0gzyDQz8nTy8/X2cgg08nT1MXNwcg=?=» NIL «root» «searchengines.ru»)) ((«=?KOI8-R?B?5s/S1c0gzyDQz8nTy8/X2cgg08nT1MXNwcg=?=» NIL «root» «searchengines.ru»)) ((NIL NIL «master» «skazkin.ru»)) NIL NIL NIL «<200911200935.b8f41f512510@forum.searchengines.ru>«) FLAGS () INTERNALDATE «20-Nov-2009 09:58:38 +0000» RFC822.SIZE 4045)
. OK Success

В целом конечно понятно — но хотелось бы ещё понятнее

. fetch 7 (body[header.fields (from to subject date)])

Выбираем только интересные заголовки:

* 7 FETCH (BODY[HEADER.FIELDS (from to subject date)] {334}
Date: Fri, 20 Nov 2009 09:58:35 +0000
To: master@skazkin.ru
From: =?windows-1251?q?=D4=EE=F0=F3=EC_=EE_=EF=EE=E8=F1=EA=EE=E2=FB=F5_=F1=E8=F1=F2=E5=EC=E0=F5?=

Subject: =?windows-1251?q?=CE=F2=E2=E5=F2=E8=F2=FC_=ED=E0_=F1=EE=EE=E1=F9=E5=ED=E8=E5_=27=F0=E0=E1=EE=F2=E5=ED=EA=E0_=ED=E0_1_=E1=E0=EA=F1=27?=

Прочитаем тело письма:

. fetch 7 rfc822.text

Вот и оно:

* 7 FETCH (RFC822.TEXT {1299}
…сообщение в кодировке windows-1251
. OK Success

В целом, как видите, нет ничего сложного чтобы управляться с консольными выводами POP3 и IMAP.

Разница только в том, что IMAP — понавороченнее, а POP3 обладает весьма урезанным функционалом — и позволяет читать только почту, которая лежит в папке INBOX.

Вспомогательная информация:

IMAP RFC

Самая эффективная защита от парсинга

Самая эффективная зашита от парсинга (то есть защита от парсинга — я кстати вылез в топ гугл по запросу этому) — у сайта film.ru . Они просто отключили сайт когда я их начал парсить.

Развожу руками.