Как стать автором
Поиск
Написать публикацию
Обновить

Критика статьи OpenCL. Практика.

Здравствуйте хабрики! Очень смешное у вас название.

Во-первых, автор ошибается, когда пишет: «Далее выберем устройство (у меня в системе оно всего одно, но на будущее пусть в нашей программе используется устройство с максимальным числом FLOPS).» т.к. он забыл то, что OCL может работать как с GPU, так и с CPU.

Ещё вот эта выделенная жирным шрифтом фраза должна быть помещена гораздо выше, не к оператору oclLoadProgSource, а к oclGetMaxFlopsDev: "ВНИМАНИЕ!!! oclLoadProgSource — не является функцией OpenCL API, а находятся в вспомогательной библиотеке, поставляемой вместе с Nvidia Computing SDK.". Почему? Да потому, что в oclGetMaxFlopsDev я сразу встретился с проблемой, что нет такой функции! А вот узнал, что оказывается она всё таки есть, через пару абзацев.

К предыдущему абзацу. Почему собственно не приведено имя библеотеки? Или её надо самому найти, пользуюсь экстросенсурными способностими? Чтоб не искали — это oclUtils.cpp.

Нужна ссылка на драйверы. Как же без этого? Я ввёл первую строчку и бабах, оказалось, что в системе нет OpenCL.dll.

Статья, конечно, является едвали не единственным описанием OpenCL на практике и на русском, но надо её немного подправить.

Скверная проблема — изменение размера шрифтов в Chrome; или как не ослепнуть за большим разрешением.

Наверняка не я один сталкивался с проблемой изменения размера шрифта фиксированной величины в google chrome на windows. А проблема ведь для многих может быть насущной, вот например у меня 20 дюймовый монитор с разрешением 1680х1050, размер пикселя соответственно небольшой, и читать текст 13ого размера очень неудобно, да что там, ослепнуть можно! «мне бы по-больше, а?»

В общем нашел 2 способа, как это можно ее решить:
первый — в папке C:\Users\имя пользователя\AppData\Local\Google\Chrome\User Data\Default или вместо Users может быть Documents and Settings есть файл Preferences, открываете его блокнотом. В самом конце находите

«webkit»: {
«webprefs»: {
«default_fixed_font_size»: 17,
«default_font_size»: 18,
«fixed_font_family»: «Bitstream Vera Sans Mono»,
«inspector_settings»: «lastActivePanel:string:elements\n»,
«minimum_font_size»: 14,
«minimum_logical_font_siz»: 14,
«sansserif_font_family»: «Times New Roman»,
«serif_font_family»: «Arial»,
«standard_font_is_serif»: false,
«text_areas_are_resizable»: true,
«uses_universal_detector»: true

изменяете там цифры, сохраняете — все)

ps: может быть вариант(у меня так и было), что там следующее:

«webkit»: {
«webprefs»: {
«uses_universal_detector»: true

если будет именно это, то приведите к виду указанному выше(возможно придется удалить строчку «uses_universal_detector»: true

так, это был первый способ.

Теперь способ №2:
Ставите последнюю DEV(тестовую) версию, скачать ее можно отсюда
Там появляется поддержка так называемых расширений. Добавляется это расширение
Теперь при запуске хрома, можете изменить размер страниц в нижней левой части экрана(прямо над пуском). Это изменение будет действовать до закрытия браузера. Т.е. до закрытия браузера все страницы будут открываться именно в том масштабе, который выбрали в этом расширении. При новом запуске придется выбрать заново.

замечания по первому способу
при изменении шрифтов, у вас на большинстве страниц могут вылазить шрифты за рамки элементов страниц, будет смотреться «не очень») Не забывайте ставить разные значения в минимальных и максимальных размерах, а то будет смотреться также.

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

Так же имеется расширение 125% zoom, которое изменяет навсегда(ну почти) размер страниц, думаю понятно насколько. Нашел я его тут, в осуждении какой-то статьи в разделе хрома.

кстати на линуксе всего этого копро нету — не удивительно кстати

Искусственный Интеллект AND or VS. Человек?!

Трудно что-либо предвидеть, а уж особенно будущее. Нильс Бор.

Ну начнем, пожалуй, не с будущего, а с прошлого. Что вам приходит на ум, когда вы видите дату 29 августа 1997 года? Ничего? А если я сообщу вам, что в этот день в Мичигане из-за неэкономичности была закрыта старейшая в США АЭС, а на Украине открылся конвент фантастов «ФАНКОН-97»? Опять на ум ничего не приходит? А если подобрать ассоциации к этим событиям: атомная энергия, научная фантастика? Опять мимо? Ну ладно, не буду больше вас томить – дело в том, что именно 29 августа 1997 года, согласно знаменитой тетралогии о терминаторах, суперкомпьютер Министерства Обороны США SkyNet, спроектированный Cyberdyne Systems Corporation для управления системой противоракетной обороны США, обретает сознание и начинают разворачиваться события, которые нам хорошо известны.

К чему такая преамбула? – резонно спросите вы. А к тому, что, возможно, такое развитие событий не так уж и фантастично, как казалось нам в те золотые школьные годы, когда мы раз за разом просматривали этот фильм на VHS. Хотя, скорее всего, было наоборот – тогда мы действительно могли в это поверить, но потом, повзрослев, вместе с новыми знаниями приобрели и новое чувство – скептицизм к подобного рода вещам.

Так давайте поспорим с Нильсом, вернемся в те золотые годы и с высоты полученных за это время знаний пофантазируем о нашем будущем, об отношении между искусственным интеллектом (далее-ИИ) и человеком, а может быть даже действительно предвидим его…

История человечества в основном – история идей. Герберт Уэллс.

Первые исследования, относящиеся к проблемам искусственного интеллекта, были предприняты почти сразу же после появления вычислительных машин. Само название новой науки возникло в конце 60-х годов XX века, а в 1969 году в Вашингтоне (США) состоялась первая Всемирная конференция по искусственному интеллекту.

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

Вроде бы цели благие – создать интеллект на подобие человеческого, отправить его вместо себя на работу, а самому за заработанные роботом с ИИ деньги нежиться на золотистом пляже Черного моря (а кто сказал, что робот будет зарабатывать больше вас?). При этом такого робота можно задействовать в опасных сферах деятельности человека, обезопасив тем самым жизнь этого самого человека. Хочу заметить, что речь не идет о современных роботах, типа сапера и т.п., где не требуется применение интеллекта. Речь идет о роботах с ИИ, которые бы смогли заменить на «боевом» посту человека, а главное – его интеллектуальную деятельность вкупе с функциональными возможностями человека, а, может быть, даже превосходящими его. Самый типичный пример, на мой взгляд, робот-полицейский. Вот только тут сразу появляется много нюансов. Один из них – можно ли считать робота с ИИ особой формой жизни, только не на основе углерода, а, скажем, кремния и, пожалуй, главный вопрос – можно ли считать его личностью. Если утвердительно ответить на этот вопрос (а для этого есть все основания), то мы углубимся еще дальше – какое право мы имеем эксплуатировать роботов с ИИ, рисковать их жизнями и т.д. Ведь даже у животных в современном цивилизованном обществе есть права. На практике выходит создание нового рабовладельческого строя (не зря говорят, что история развивается по спирали), а в таком строе, как мы знаем из уроков истории, нередко случались восстания.

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

Ни у одного народа вера в бессмертие не была так силь на, как у кельтов; у них можно было занимать деньги, с тем что возвратишь их в ином мире. Генрих Гейне.

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

Но это все кажется нам далеким будущим, а что же ждет нас в ближайшем? Специалисты одной из самых влиятельных в мире аналитических компаний в области технологических исследований и консалтинга — Gartner — сделали ряд предсказаний на предстоящее десятилетие, касающихся информационных технологий. Так, к 2015 году 40% нынешних должностей в сфере IT заменят средства автоматизации. Организаторы бизнес-процессов смогут менять потоки работ в них при помощи графических интерфейсов без участия специалистов IT-отдела. Это, конечно, приведет к отмиранию многих современных должностей.

Какой же из всего этого можно сделать вывод? А вот какой – cам по себе ИИ, как и большинство изобретений человечества, не хорош и не плох. Самое важное – осознание человеком ответственности за использование своих изобретений перед окружающим миром. Мне кажется, что хотя и лаконичный, но достаточно емкий вывод, жаль только, что не мой, но хорошо, что и не искусственного интеллекта :)

З. Ы. Наслаждайтесь общением с людьми и мыслями друг друга. Возможно, скоро за нас будут думать ДРУГИЕ (звучит финальная мелодия из Терминатора 2).

nnCron — в бой идут одни старики

Вчера задумался, почему я, имея самую простую клавиатуру за 200 р., должен чувствовать себя ущербным по отношению к обладателям мультимедиа-клавиатур за тысячи рублей, утыканых со всех сторон дополнительными кнопками. Хочу назначать на любые клавиши любые приложения и всё тут. Для начала хотелось бы запускать музыку, видео, почту и браузер.

В жизни всегда есть место для творчества — на сцене появляется nnCron. Что это такое? Если сказать, что это планировщик или твикер — это будет верно, но не до конца. nnCron — это швейцарский нож для управления ресурсами вашего компьютера. При своём мало-размере возможности его впечатляют. Лучше об этом почитать на сайте производителя.

Минимум, что он умеет — это запускать приложения по времени (sheduler), при этом синтаксис команд совместим с Unix crontab. Максимум — это встроеный скриптовый язык. При этом не обязательно для добавления задачи писать макрос, у nnCron есть и GUI, через который можно управлять заданиями в привычной для многих форме. По мне, так все остальные твикеры и шедуллеры для виндовз — идут строем в ресайкл бин.

А теперь — слайды :)

Назначаю горячую клавишу <Ctrl+F12> на запуск The Bat

#( Bat-start
AsLoggedUser
WatchHotKey: “^{F12}”
Action:
StartIn: “C:\Program Files\The Bat!”
ShowNormal NormalPriority
START-APP: C:\Program Files\The Bat!\thebat.exe
)#


Назначаю горячую клавишу <Ctrl+F11> на запуск Mozilla Firefox
Тут немного интереснее — если Firefox уже запущен, то nnCron не будет запускать копию, а сделает окно Firefox активным.

#( Firefox-start
AsLoggedUser
WatchHotKey: “^{F11}”
Action:
PROC-EXIST: “firefox.exe”
IF
WIN-ACTIVATE: “/.*firefox/i”
ELSE
StartIn: “C:\Program Files\Mozilla Firefox”
ShowNormal NormalPriority
START-APP: C:\Program Files\Mozilla Firefox\firefox.exe
THEN
)#


Поработал — отдохни.

Назначаю хоткеи <Ctrl+F10> на воспроизведение DVD (используется Mplayer) и <Ctrl+F9> — на воспроизведение mp3 (Winamp).
При запуске Winamp, неплохо было бы, чтобы он сразу стартовал проигрывание трека, я надеялся нагуглить какие-нибудь параметры командной строки для оного, но нашёл это — плагин AutoPlay. Всяко неплохо, даже ещё интереснее. И наконец, чтобы уж до конца довести идею “once-click”, — при запуске DVD будем убивать Winamp, зачем — обьяснять не буду.

#( Mplayer-DVD-autoplay
AsLoggedUser
WatchHotKey: “^{F10}”
Action:
StartIn: “C:\mplayer”
ShowNormal HighPriority
KILL: “mplayer.exe”
KILL: “winamp.exe”
START-APP: C:\mplayer\mplayer.exe dvd://0 -dvd-device e: -fs
)#


#( Winamp-start
AsLoggedUser
WatchHotKey: “^{F9}”
Action:
StartIn: “C:\Program Files\Winamp”
ShowNormal NormalPriority
KILL: “winamp.exe”
START-APP: C:\Program Files\Winamp\winamp.exe
)#


Всё, софт для мультимедия-клавиатуры инсталлирован:)
Обязательно рисую маркером пиктограммы на клаве — без маркера концепция handmade кажется незавершённой, а без пиктограмм никто не поверит, что у меня настоящая мультимедия-клавиатура.

image

Домен.РФ, кириллица в URL и кодировки: добро пожаловать в ад.

Здравствуйте. Итак, я человек которому очень не нравится английский язык, принципиально. А потому, я, ещё задолго до придумывания сильными и умными мира сего домена.РФ, загорелся целью сделать себе сайт с кириллическими ЧПУ. Сайт уже существует, хотя и параллельно дорабатывается до вменяемого состояния.

Но вот началось регистрация доменов.РФ, и скоро, надо полагать, там появятся первые сайты. И скорее всего владельцы кириллического домена захотят чтобы url из сайта выглядел не так: http://домен.рф/news/my_otkrylis/, а вот так: http://домен.рф/новости/мы_открылись/
А так, как я начала реализовывать нечто подобное, то думаю что мой опыт будет многим полезен. Особенно программистам и SEO специалистам — именно для них тут будет много весёлых, занимательных открытий, после которых у некоторых наверняка возникнет желание сделать что-то нехорошее с теми массовиками-затейниками, которые всё это придумали. Если бы я сам знал, что меня ждёт, никогда не стал бы так извращаться и сделал бы всё на латинице.

Итак, поехали.

ЧПУ и сам кириллический URL.


Как обстоит дело с обычными GET запросами вроде ?категория=новости&статья=мы_открылись, я не знаю, но думаю с тут проблем быть не должно. А вот если вы захотите сделать ЧПУ, то боюсь, что придётся вам его делать в коде самой страницы, а не через mod_rewrite, ибо последний кириллицу не понимает. В .htaccess крякозяблы приходят, вместо русских букв. И подружить .htaccess с кириллицей у меня так и не получилось (если кому-то это удастся, то пожалуйста, дайте знать как).

Но тут есть ещё один подводный камень. У меня весь сайт в кодировке cp1251. А от браузеров URL всегда приходит в UTF-8 кодировке, независимо от ОС и браузера. Но в случае, если вы будете делать header(), то тут прежде всего нужно будет перекодировать в юникод. Ибо после header() заголовок посылается уже в той кодировке, которая была в переданной строке, а не в юникоде.
И тут главное чтобы ie6 наконец уже окончательно канул в бездну, так как он, после header`а, в адресной строке отображает крякозяблы. Благо это только его баг, во всех остальных браузерах вроде всё работает как нужно.

Однако, боюсь, что дальше этого пойти не удастся. Я имею ввиду то, что внутренние ссылки не поддерживают кириллицу как минимум в ie6-8 (учитывая что ie занимает всё-таки лидирующие позиции среди пользователей интернета, да и пользоваться этими доменами будут в основном люди не всегда знающие о существовании других браузеров, кроме ie, то думаю что именно на этот браузер нужно ориентироваться в первую очередь).
Т.е. если есть ссылка с name="якорь", то при переходе с http://домен.рф/новости, на http://домен.рф/новости#якорь — страница перезагрузится. Так что клиентов, которые хотят чтобы на их свежекупленном домене.РФ не было латинских символов в принципе, придётся убеждать в необходимости латиницы в якорях.
Но это ещё цветочки, а ягодки пойдут дальше…

Кириллические URL`ы, поисковики и системы размещения рекламы.


А вот здесь начинается настоящий ад. Тут очень многое зависит от настроек сервера. Например, при работе сайта в сети обнаружились очень интересные особенности. А именно, при индексации некоторыми поисковиками и система размещения рекламы страниц с кириллическими URL`ами, сервер им выдавал 406 код ошибки, хотя сам скрипт всё обрабатывал как нужно, делал нужные sql запросы и получал всё необходимое содержимое. И оказалось, что в utf-8 кодировке URL приходит только от браузеров, а все остальные шлют запросы в том виде, в каком им хочется. В частности была и такая проблема с Яндексом. Вот часть ответа из службы поддержки по поводу причины такого явления:
Проверка показывает, что сервер формирует данный код ответа на запрос
документов в типе text/html. То есть, по какой-то причине, сервер, отдавая
text/html, считает, что он не может быть обработан роботом и формирует ответ
406 Not Acceptable.
Браузерам (например FireFox) отдается страница с кодом 200, так как они
отправляют запрос, который предполагает, что сервер может вернуть любой тип
контента, если нет возможности вернуть text/html, application/xhtml+xml или
application/xml (Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8).


Собственно после этого, я плюнул и решил перевести всё-таки все страницы сайта utf-8 кодировку. Но, как ни странно, это не помогло. А дело оказалось в том, что для правильного ответа сервера нужно не просто, чтобы сами ссылки были в юникоде, но и чтобы они были закодированы через urlencode(), иначе — всё равно 406 ошибка. В этом отношении меня порадовал только лишь Google. Ему всегда возвращается 200 код, в какой бы кодировке что не находилось.

Таким образом, для того, чтобы некоторые избранные могли проиндексировать вашу страницу, нужно чтобы они переходили по URL находящемуся в utf-8 кодировке и обработанному через urlencode(). Строка в win-1251 и urlencode() тоже не пройдёт, только юникод.

При этом обработать ссылки через urlencode() не всегда легко так легко. К примеру, что будет, если контент сайта внутри заполняется кем-то, кто не знает вообще про кодировки ничего и он создаст просто обычную внутреннюю ссылку вида href="/новости"? Более того, получается что таким системам и людям нужно подсовывать разные ссылки — людям обычные ссылки, а роботам ссылки после urlencode()? Да и с и с точки зрения того же Яндекса, будут ли одинаковыми ссылки http://домен.рф/новости и http://домен.рф/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8? Не уверен. А при публикации ссылок на внешних ресурсах как быть?
Лично я решил эту проблему так: у меня просто определяется, что за клиент передо мной: из «чёрного списка», в котором все те, кому 406-ая ошибка когда надо и не надо возвращается, или нет. В первом случае при выводе в меню ссылки обрабатываются через urlencode() и весь основной контент страницы так же пропускается через функцию, которая ищет внутренние ссылки, и, если находит, то заменяет их на обработанные в urlencode().

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

Ну и на последок ещё одна «нехорошесть».


Ну вот всё описанное выше вы решили, и запустили сайт в сеть. Поставили счетчики статистики и вроде бы все рады, пока клиент не просмотрит статистику через cpannel и не увидит примерно следующее: /\xd0\xbd\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x81\xd1\x82\xd0\xb8. Или на топ.мейл увидит всё те же самые URL`ы, закодированные через urlencode(). И вот попробуйте ему в таком случае объяснить, что вы тут не властны, и что это вовсе не то, что вы хотите скрыть от него кто и какие страницы сайта посещал. А ведь согласитесь, в таком представлении даже сведущему человеку не сразу понять что это за такое, а уж простому обывателю и подавно.

Таким образом, домен.РФ может и существует, может скоро и появятся сайты, где в адресной строке на латинице будет только http, но вот только проблем с такими сайтами будет очень много, как у их разработчиков, так и у оптимизаторов. Ну а пока все роботы и системы к этому привыкнут, подправят свои алгоритмы и начнут выводить и обрабатывать кириллические URL`ы как нужно, пройдёт немало времени.
Надеюсь я этим материалом помогу тем несчастным, которые будут всё-таки вынуждены создавать полностью кириллические сайты.

P.S. Этой мой первый пост на хабре. Извиняюсь, если что-то запостил не так и спасибо всем вам за прочтение. Я буду очень получить инвайт за этот пост)))

BlueJ или ООП для начинающих.

Hello Habrapeople!

Поискав информацию о BlueJ в блогах Хабра, на свое удивление, ничего не обнаружил. Как впрочем и удивлен незначительному количеству статей о JAVA. Значит есть возможность о чем рассказать.:)

image

BlueJ.


Программа была разработана университетами Deakin University (Мельбурн, Австралия) и University of Kent (Canterbury, Великобритания) на основе имеющейся среды Blue, как часть исследовательского проекта по обучению ООП начинающих программистов при поддержке Sun Microsystems. Иными словами, BlueJ(голубая сойка) — это среда программирования для студентов, начинающих изучать JAVA и основы ООП. Особый акцент был сделан на визуализацию и методы взаимодействия для создания интерактивной среды, обеспечивающей понимание того, как все устроено. Программа классифицируется как freeware, работает в любой операционной системе и скачать ее можно здесь.

image

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

Редактируемая линия с тултипом в Google Maps

Наконец-то, нашлась свободная минутка, чтобы поделиться одной из своих наработок. И попытать счастье, может инвайт перепадёт :)

Предисловие.
Примерно месяца четыре назад возникла необходимость для своей организации создать карту-схему объектов с возможностью редактирования. Задачу решил успешно, склеив ExtJS и GMap. Получилась мини ГИС для локального использования, к сожалению, не мо-гу поделиться ссылкой, т.к. информация не для общего пользования, да и сама система крутится в локалке.

Суть.
Итак, в процессе создания столкнулся с необходимостью отображать линии между объек-тами. При этом линии должны отвечать нескольким требованиям:
  1. Возможность редактировать с сохранением в базу.
  2. У каждой линии должна быть подпись. Т.к. линий было очень много, пришлось подписи сделать всплывающими.
  3. При редактировании линии должна быть возможность добавлять точки и изломы.

В итоге, я написал небольшой «класс», который хочу представить на ваш суд. Надеюсь кому-нибудь пригодиться. При желании его можно доработать и улучшить.

Исходник: labeledline2.js

Пример: тут

  1. Сохраняется только в памяти, не в базу, убрал эту часть кода.
  2. Подпись появляется при наведении курсора.
  3. Реализована возможность программного добавления точек, а также визуальное ре-дактирование. После клика на линии она переходит в режим редактирования, где можно удалять точки, добавлять изломы и изменять положение существующих то-чек. По второму клику выдается запрос на сохранение и линия переходит в режим просмотра.


Спасибо за внимание!

C#. Argument null validator

Как избавиться от строковых констант при проверке аргументов на null, то есть избавиться от хард-кодед констант. Имеем пример:

    private static void FillStringBuilderCommon(StringBuilder stringBuilder, String stringArgument)
    {
      if (stringBuilder == null)
      {
        throw new ArgumentNullException("stringBuilder");      }
      if (stringArgument == null)
      {
        throw new ArgumentNullException("stringArgument");
      }

      stringBuilder.Append(stringArgument);
    }

* This source code was highlighted with Source Code Highlighter
.



Выделенное было давно не по душе, и я искал пути, как бы сделать прямое вытягивание имен аргументов. Первоначальную идею задал топик на rsdn про удобное получение имени свойства. В результате чего было получен следующий Extension method.

  static class Extensions
  {
    public static string GetName<TResult>(this Expression<Func<TResult>> expression)
    {
      return ((MemberExpression)expression.Body).Member.Name;
    }
  }

* This source code was highlighted with Source Code Highlighter
.
Что привело к следующему варианту функции проверки аргумента на null

    public static void NullValidate1<TResult>(Expression<Func<TResult>> argumentExpresion)
      where TResult : class
    {
      if (argumentExpresion.Compile()() == null)
      {
        throw new ArgumentNullException(argumentExpresion.GetName());
      }
    }

* This source code was highlighted with Source Code Highlighter
.

Жутко медленная вещь получилась, я вам скажу, что естественно меня не уcтраивало ни коим образом. Данная функция позволяет, кратко и понятно записать вызов валидации, например:

      Tools.NullValidate1(() => stringBuilder);

* This source code was highlighted with Source Code Highlighter
.


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

    [DebuggerStepThrough]
    public static void NullValidate2<TResult>(Expression<Func<TResult>> argumentExpresion, TResult argument)
      where TResult : class
    {
      if (argument == null)
      {
        throw new ArgumentNullException(argumentExpresion.GetName());
      }
    }

* This source code was highlighted with Source Code Highlighter
.


Получилось лучше. Стало гораздо быстрее работать, но, забегая вперед, недостаточно быстро. Вызов же стал немногим сложнее. На один аргумент.

      Tools.NullValidate1(() => stringBuilder, stringBuilder);

* This source code was highlighted with Source Code Highlighter
.


Как показали тесты на произволительность, потери были значительными, на порядки. Вся проблема заключалась в первом аргументе. Объяснять «почему?» думаю не надо.
В итоге пришел к заключительному виду, коим пользуюсь сейчас:

    [DebuggerStepThrough]
    public static void NullValidate<TResult>(TResult argument, Func<Expression<Func<TResult>>> nameRetreiver)
      where TResult : class
    {
      if (argument == null)
      {
        throw new ArgumentNullException(nameRetreiver().GetName());
      }
    }

* This source code was highlighted with Source Code Highlighter
.

Вызывается так:

Tools.NullValidate(stringBuilder, () => (() => stringBuilder));

Таким образом тестовая функция в целом выглядит следующим образом:

    private static void FillStringBuilderExpression(StringBuilder stringBuilder, String stringArgument)
    {
      Tools.NullValidate(stringArgument, () => (() => stringArgument));
      Tools.NullValidate(stringBuilder, () => (() => stringBuilder));

      stringBuilder.Append(stringArgument);
    }

* This source code was highlighted with Source Code Highlighter
.


Результаты перформанс теста:

[FillStringBuilderCommon] StringBuilder length: 3000000
[FillStringBuilderCommon]: 00:00:00.0532751
[FillStringBuilderExpression2] StringBuilder length: 3000000
[FillStringBuilderExpression2]: 00:00:07.2346404
[FillStringBuilderExpression] StringBuilder length: 3000000
[FillStringBuilderExpression]: 00:00:00.1308366
Press any key to exit...


Код теста:

   static void Main(string[] args)
    {
      MeasureFunc((sb, arg) => FillStringBuilderCommon(sb, arg));

      MeasureFunc((sb, arg) => FillStringBuilderExpression2(sb, arg));
      MeasureFunc((sb, arg) => FillStringBuilderExpression(sb, arg));

      Console.WriteLine("Press any key to exit...");
      Console.Read();
    }

    private static void MeasureFunc(Expression<Action<StringBuilder, string>> functionExpression)
    {
      const uint limit = 1000000;
      string title = string.Format("[{0}]", ((MethodCallExpression)functionExpression.Body).Method.Name);

      Action<StringBuilder, string> function = functionExpression.Compile();

      StringBuilder stringBuilder = new StringBuilder();
      String stringArgument = "saa";
      
      Stopwatch stopwatch = new Stopwatch();
      stopwatch.Start();

      uint index = 0;
      while (index++ < limit)
      {
        function(stringBuilder, stringArgument);
      }

      stopwatch.Stop();

      Console.WriteLine(title + " StringBuilder length: " + stringBuilder.Length);
      Console.WriteLine(title + ": " + stopwatch.Elapsed);
    }

* This source code was highlighted with Source Code Highlighter
.


С уважением, Александр.

Составление технического задания (ТЗ) на сайт с использованием ГОСТ.

image

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

Читать дальше →

Размышления об ООП в Javascript.

С некоторых пор я озаботился поиском интересных технологий веб-разработки, в частности довольно плотно занялся изучением специфических решений на JavaScript + HTML + Java/Flash(ActionScript). Именно так я, кстати, и забрёл на хабрахабр.

Я нашёл много интересного, но обо всём по порядку. Я обнаружил, что практически каждая уважающая себя библиотека в той или иной форме пытается реализовать некое подобие классового подхода (или «классовой парадигмы», если так понятнее) на JS — это и MooTools, и qooxdoo, и Prototype (хотя это и мало соотносится с его названием) и многие другие. Я мог бы поспорить с тем, что ООП в «классическом» (т.е. классовом) его понимании так уж необходим в JS — сильно развитые возможности функционального программирования в этом языке могут быть гораздо эффективнее — но раз классический подход реализуется, значит он востребован, что, в общем-то, не особо удивляет, т.к. ну привыкли мы уже так писать, и не особо хочется переучиваться.

Меж тем, как я заметил, присутствует и ещё один неприятный аспект — реализация некоего «классического ООП JavaScript», под которым понимается обычно связка прототипизации и замыканий. Такой подход выглядит не менее однобоко, чем реализация классовой парадигмы, однако воспринимается более «родственно» языку. Такой подход имеет один очень существенный недостаток — при реализации секции private функции плодятся как кролики, и память становится их капустой. При том, что в браузере пользователя обычно открыто штук по десять вкладок с сайтами, каждый из которых делает нечто похожее, производительность от этого здорово падает.

О том, как надо бы использовать JS, можно прочитать хотя бы здесь. Но я уверен, что люди по прежнему будут пытаться сделать JS объектно-ориентированным. Поэтому задумаемся над тем, как дать таким людям возможность использовать язык привычным «объектным» способом, покуда они не начали постигать его «функциональную» суть.

У всех реализаций есть один существенный недостаток — их private и protected (если таковые имеются) поля и методы являются обычным фэйком, и, как правило, не составляет никаких проблем просто подменить arguments.callee._class на ClassWidthPrivateMethod — и вуа-ля, мы вызвали приватный метод извне. Язык, который позволяет что-то реализовать, с тем же успехом позволяет всё это обойти. Не менее неприятным моментом любой реализации является то, что заявления типа «при классическом подходе создаётся дикое количество экземпляров функций» полностью разбиваются о тот факт, что сама реализация создаёт внутри своих систем защиты не меньшее, а то и большее количество экземпляров одной и той же функции, при том, как правило, не самой маленькой. Так, спрашивается, зачем всё это нужно.

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

class.stanalone.js
run.init.js

Первый файл — это библиотека, предназначенная для standalone использования (в оригинальной реализации она является частью нашей платформы JACK), а второй — скрипт для тестирования.

Библиотека пока ещё достаточно сыра, да и её системы защиты, наверняка, можно обойти (кстати, если кто-то это сделает, то сообщите на akaj@evounit.ru, что бы я мог исправить ошибки). Но изучив её исходный код вы, возможно, что-то подчерпнёте для себя. Или, может быть, она просто вам пригодится в работе.

Интерфейс библиотека выглядит так:

Class() — функция для объявления нового класса.
Class.GUID() — функция возвращает уникальный идентификатор класса (может пригодится зачем).
Class.Self() — функция возвращает ссылку на сам класс.
Class.Superior() — функция возвращает ссылку на родительский класс.
Class.Interface(_index) — функция возвращает ссылку на интерфейс класса с индексом _index.
Class.Interfaces() — функция возвращает массив интерфейсов класса.
Class.ConstructorFunction() — функция вызывает конструктор применимо к своему контексту this.
Class.Create(...) — функция создаёт экземпляр класса и вызывает его конструктор с переданными параметрами.
Class.HeritableNames() — функция возвращает список членов класса.
Class.Extends(_superior) — функция указывает родительский класс.
Class.Implements(_interfaces|_interface1, _interface2, ...) — функция указывает интерфейсы класса. Аргументы можно передавать напрямую или в виде массива.
Class.OffspringOf(_ancestor) — функция проверяет, является ли класс потомком класса _ancestor.
Class.Constructor(_function) — функция указывает конструктор класса.
Class.Define(_list) — функция указывает члены прототипа класса. _list должен быть объектом, поля которого будут использованы для присвоения.
Class.Var(_access, _list) — функция указывает переменные класса. Доступ к переменной можно полчить в виде ClassInstance.varName() для считывания и ClassInstance.varName(varValue) для записи значения. _access должен быть строкой, которая может содержать следующие лексеммы: private (закрытые члены класса), protected (защищённые), public (открытые), static (статические члены или члены класса) и readonly (член доступен только для чтения).
Class.Method(_access, _list) — функция указывает методы класса. Объявлять метод readonly не имеет смысла.
Class.HasMethod(_function) — функция проверяет, есть ли у класса указанный метод.

Class.Create создаёт ClassInstance, которые обладает следующим интерфейсом:

ClassInstance.GUID() — функция возвращает уникальные идентификатор объекта.
ClassInstance.Self() — функция возвращает класс объекта.
ClassInstance.Superior() — функция возвращает ClassWrapper (см. ниже) для родительского класса.
ClassInstance.Interface(_index) — функция возвращает ClassWrapper для интерфейса с индексом _index.
ClassInstance.Interfaces() — функция возвращает массив ClassWrapper'ов для интерфейсов.
ClassInstance.Call(_name, ...) — функция осуществляет вызов метода по его имени.
ClassInstance.Wrap(_class) — функция создаёт ClassWrapper для класса _class.

ClassWrapper — этот оболочка для ClassInstance, представляющая её экземпляром указанного класса. Интерфейс оболочки выглядит следующим образом:
ClassWrapper.Self() — функция возвращает ссылку на класс оболчки.
ClassWrapper.Superior() — функция возвращает ClassWrapper для родительского класса класса оболочки.
ClassWrapper.Interface(_index) — функция возвращает ClassWrapper для интерфейса оболочки с индексом _index.
ClassWrapper.Interfaces() — функция возвращает массив ClassWrapper'ов для интерфейсов оболочки.
ClassWrapper.Construct(...) — функция вызывает конструктор класса оболочки применимо к ClassInstance.
ClassWrapper.Call(_method, ...) — функция вызывает метод прототипа класса оболочки применимо к ClassInstance.

Библиотека будет расширяться и дорабатываться. Я по прежнему рекомендую использовать возможность функционального программирования в JS, но библиотека — в помощь. По все вопросам вы можете писать на akaj@evounit.ru.

Спасибо за ваше внимание,
Александр Кай.

Говорит Лондон — европейские стартапы должны работать больше и лучше

Это перевод интервью, взятого у венчурного инвестора из Лондона. European startups need to work as hard as Valley ones – or forget it

Одна из проблем, стоящих перед инвесторами и основателями компаний — это нанять таланты. На ранних стадиях инвестирования состав команды может быть единственным критерием для принятия решения об инвестировании. То, что становится скоро понятным после занятий венчурным инвестированием, так это то, что абсолютное количество талантов в Европе/Великобритании меньше, чем в США. Рабочая этика (отношение к работе) также сильно различается.

Каждый, кто был в Силиконовой Долине, может заметить — там работают все время.

Это может показаться глупым, фанатичным, безумным, но с точки зрения выживания экосистемы — эта стратегия непобедима. Если вы хотите построить компанию, и быть на волне инноваций, то это занятость 24/7, а не бизнес в свободное время. Напротив, офисы лондонских стартапов почти все пустые после полседьмого вечера.

Где чувство срочности? Где жажда скорости? Где дух соревнования, где драйв, где желание замочить конкурентов, или существующие большие корпорации?

Я понимаю, что такое баланс между жизнью и работой, но с точки зрения инвестора, я бесконечно более счастлив работать с парнями, которые рвут свои жопы на работе, и в конце концов, добиваются своего!

Не поймите меня неправильно (don’t get me wrong!), это не означает «быть в офисе». Офисные часы — только одно измерение. Я просто говорю о работе в целом — и просто быть онлайн, отвечать, присутствовать (twitter, IM, email, апдейты веб-сайта, релизы, обновления и т.д.).

Это всего лишь мое (похоже, непопулярное) мнение, но в Силиконовой Долине стиль мышления примерно такой: “почему бы нам не сделать это прямо сейчас” или “дайте закончить мне это до того, как я уйду из офиса” и т.п.

Здесь культура «запустить и итерировать» (launch and iterate), а в Европе полагаются на устаревшую модель релизов. Это хорошо для ненапряжного лайфстайла, но плохо для создания быстро двигающихся вперед компаний. А быстрые компании требуют людей, которые их двигают в режиме 24х7.

По контрасту, что я наблюдаю в Европе: это мышление в стиле “это может подождать” или “не проблема, если мы позаботимся об этом завтра” или “да, сэр, это поставлено на очередь”. Вот в чем большая разница в восприятии мира!

И эта проблема не в том, в какие часы парни сидят на работе в офисе (или даже вне офиса). В Силиконовой Долине, даже если чуваки не работают или не на работе, они все равно работают. Даже когда они ходят в клуб, на вечеринки, за обедом или просто где-то зависают… Они работают, потому что каждый, с кем они зависают, находится в теме (in the industry), и поэтому это общение имеет прямое отношение к работе.

Ваши лучшие друзья, со-основатели, конкуренты, партнеры по развитию бизнеса, ваши юристы или спонсоры мероприятий. Вы знакомитесь, перекидываетесь парой слов (hook up with) или женитесь на ваших коллегах, ваших менеджерах по развитию бизнеса, пиарщиках. Это очень маленький мир, и возможно, ненормальный (хотя это работало для многих других случаев, иначе бы Долина погрязла бы дрязгах разводов и юридических процессов).

Но с точки зрения инвестирования, распространения знания вирусным путем, такая среда экстремально ценна. Это как одна большая чашка Петри. Сверх-мега-корпорация стартапов, где люди могут циркулировать между Google, Yahoo! Apple и другими компаниями. Это заставляет всех делать свою работу, думать о ней, буззить (buzzing) о ней все время.

Вместо этого, в Европе мы имеем не только отдельную чашку Петри для лондонских старапов, стартапов севера Англии, ирландских, немецких, восточноевропейских стартапов и т.д., но и имеем свои отдельные чашки Петри для бутстрапперских (bootstrapped) стартапов, стартапов, финансируемых большими венчурными фондами, стартапов для мобайла, для медиа. А также для стартапов, которые используют агентства (кстати, в Долине ни один стартап не использует агентства) и компаний, которые говорят, что они являются стартапом, но в которых ни один человек в реальности не работает активно.

Это, кстати, касается также отношения к выходным, праздникам и вообще свободному времени.

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

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

В Великобритании отпуск составляет 20 рабочих дней (3 недели!), и каждый следит — и, главное, использует эти 4 недели. В Долине это выглядело бы абсурдом, и если бы человек брал 4 недели каждый год, то он прослыл бы слэкером (прогульщиком, slacker). Подумайте о том факте, что даже количество дней отпуска по закону отличается в Европе и США в два раза!

Верьте мне — если вы раздражены, что вы работаете как лошадь над следующим глобальным успешным проектом, то я хочу встретиться в вами и инвестировать в вас. В противном случае, если вы работаете для галочки (ticked off) и не рвете попу на работе, продолжайте в том же духе — и наслаждайтесь вашим ежегодным отпуском!

Письмо с ФСБ РФ о нарушение закона проектом QIP

Доброго времени суток, вот решил запостить тут статейку, хочется узнать Ваше мнение по данному вопросу и правильности поступка.
Устал от внедрения всяких сервсов в КИП (Слово QIP, по правилам английского произношения читается как КИП) и слива информации и личных данных в сеть, а также получение огромного количества спама. Разработчики на контакт не идут, а на форуме только прихлебатели бегают по темам и пытаются заткнуть рот тем кто говорит что то плохое про КИП. Поэтому решил использовать ко ординальные меры для решения этого вопроса, поэтому написал письмо в ФСБ РФ

«Уважаемый Александр Васильевич.
Довожу до вашего сведения о нарушении закона N152-ФЗ «О персональных данных» от 27 июля 2006 г, который нарушается интернет проектом www.qip.ru, который разрабатывает QIP Infium — мультипротокольный клиент с поддержкой внешних модулей.
В результате установке данной программы по дефолту устанавливается (меняется) стартовая страница, поисковик и регистрируются сервисы без предупреждения об этом конечного пользователя. При этом в качестве логина и пароля были приведены реальные логин и пароль пользователя на qip.ru, а также реальные данные которые указываются при регистрации QIP Infium. Из чего можно сделать вывод, что:
1. Пароли к qip.ru хранятся в базе данных в открытом виде:
2. Доступ к базе данных с паролями имеют многие проекты, а не только команда разработчиков QIP
Без ведома пользователя стал вести микроблог, на сайте mblogi.qip.ru постятся статусы пользователей джаббера с сервера квипа без ведома конечного пользователя. Удалить себя из этого прекрасного сайта не представляется возможным. Позиционируется это как «сервис ориентированный на пользователей, предпочитающих умещать события своей жизни в короткие и емкие фразы», при этом происхождение сего ёмкого контента не раскрывается. Свои статусы можно посмотреть, подставив в конец ссылки mblogi.qip.ru/people свой логин. Тоже самое происходит и с другими сервисами внедренными в программу QIP Infium, а конкретней:
1. file.qip.ru — хранение файлов, идет автоматическая авторизация на сайте без ведома пользователя.
2. love.qip.ru — знакомства, идет автоматическая авторизация на сайте без ведома пользователя, создается анкета с реальными данными указанными при регистрации QIP Infium.
3. friends.qip.ru — социальная сеть, идет автоматическая авторизация на сайте без ведома пользователя, создается анкета с реальными данными указанными при регистрации QIP Infium, личный список контактов добавляется в друзья и выводится в свободное распространение.
4. memori.qip.ru — закладки, идет автоматическая авторизация на сайте без ведома пользователя, создается анкета с реальными данными указанными при регистрации QIP Infium.
5. Хранение Логинов, паролей, настроек и истории переписке на серверах QIP Infium – происходит по дефолту
6. И многое другое.
Также разработчики использует базу логинов и паролей для создания аккаунтов на левых сайтах от имени, но без ведома пользователя — у каждого пользователя QIP Infium ВНЕЗАПНО появились аккаунты на всяких сайтах как с порнографическим содержанием так и с рекламным характером, на указынные при регистрациях данные (электронная почта) идет большое количество спама с порнографическим и рекламным характером.
При возникновении подобных тем на форуме qip.ru разработчики не как не комментируют это, подобные темы быстро закрываются.
Нечего подобного в пользовательском соглашении при установки программы не пишется. Более того, при выборе русского языка установки, предлагается безальтернативно ознакомиться с английским соглашением.
В результате всего мышее перечисленного программа QIP Infium нарушает:
1. закон N152-ФЗ «О персональных данных»
2. КоАП РФ. Статья 13.11. Нарушение установленного законом «О персональных данных» порядка сбора, хранения, использования или распространения информации о гражданах (персональных данных)
3. КоАП РФ. Статья 13.14. Разглашение информации с ограниченным доступом
4. КоАП РФ. Статья 5.39. Отказ в предоставлении гражданину информации
5. УК РФ. Статья 137. Нарушение неприкосновенности частной жизни
6. УК РФ. Статья 140. Отказ в предоставлении гражданину информации
7. УК РФ. Статья 272. Неправомерный доступ к компьютерной информации
Прошу вас принять меры но устранению данных нарушений и наказу виновных в их не соблюдении.

С уважением к Вам…
Телефон +7…

Паспорт ..........................................»

Вот и скрины
Изображение с кодом 82382 - savepic.org — сервис хранения изображений
Изображение с кодом 94670 - savepic.org — сервис хранения изображений

ЗЫ Подобное письмо отправил еще в МВД РФ и Ген Прокуратуру РФ

Теперь хочу знать ваше мнение.

простой, но звездный рейтинг на JQuery

Привет.
Появилась необходимость написания компонента на Java Script для определения рейтинга, так называемого star rating.

Подчеркну, что интересовала именно UI часть, ибо запись в БД и обработка рейтинга уже сделана на JAVA.
Сейчас на множестве блогов и новостных сайтов используется «звездный» подход для определения рейтинга комментария, статьи, новости. Удобно и приятно.
Обычно это 5 звездочек. При клике на одну из них выставляется соответственная оценка.
В сети нашел множество примеров для этого дела — на JQuery, с использованием AJAX и «обычного» JS.

Порывшись и на хабре, нашел ссылки на некоторые примеры:
http://www.fyneworks.com/jquery/star-rating/#tab-Testing
http://www.m3nt0r.de/devel/raterDemo/

Но большинство примеров показались мне усложненными. Хотелось же попроще и покороче. Пришлось заниматься велосипедизмом. Может это и неправильно в том плане, что таких примеров море в сети, но все же ой как захотелось минимализма.

И вот что из этого вышло:

1) маленький star rating с использованием JQuery, скриптовая часть занимает 57 строк;
2) CSS для затемнения звездочек при наведении мыши с помощью стандартных CSS фильтров;
3) соотвественно HTML часть для отображения всего этого безобразия.

Прошу пинать и жаловать!

JS часть

$(document).ready(function() {

var starId = "[id*='star']";
var curRatingId = "[id*='curRating']";
var starredId = "[id*='starred']";

//mouse over handling
$(starId).bind(('mouseenter'), function(){
starIndex = $(this).attr('id').substr(4);
$('#curRating' + starIndex).show();
$('#rating').hide();

});

$(starId).bind(('mouseleave'), function(){
starIndex = $(this).attr('id').substr(4);
$('#curRating' + starIndex).hide();
$('#rating').show();
});

$(starredId).bind(('mouseenter'), function(){
starIndex = $(this).attr('id').substr(7);
$('#curRating' + starIndex).show();
$('#rating').hide();
});

$(starredId).bind(('mouseleave'), function(){
starIndex = $(this).attr('id').substr(7);
$('#curRating' + starIndex).hide();
$('#rating').show();
});

//opacity handling
$(starId).click(function() {
starIndex = $(this).attr('id').substr(4);
for (var i=1;i<=starIndex;i++){
$('#star' + i).hide();
$('#starred' + i).show();
$('#starred' + i).parent().removeAttr('class');
}
$('#rating').html(starIndex);
});

$(starredId).click(function() {
starredIndex = $(this).attr('id').substr(7);
for (var i=1;i<=starredIndex;i++){
$('#starred' + i).show();
$('#star' + i).hide();
}

for (var y=parseInt(starredIndex)+1;y<=5;y++){
$('#starred' + y).hide();
$('#star' + y).show();
}
$('#rating').html(starredIndex);
});
})


один архив с HTML и CSS

http://www.easy-share.com/1908532338/star.rar

совместимость

Протестировано и работает в следующих браузерах:
FF 3.5.5; IE (7); IE8; IE6; Opera 10.00; Chrome 3.0.195.33; Safari 4 (Public Beta).
В IE 5.5 к сожалению, не работает ))

Жду ваших комментариев советов по минимизации.

А также очень интересуют отзывы тех, кто проще придумал рейтинг.

Руководство по программе восстановления паролей «John the Ripper»

Как-то у меня появилась необходимость подобрать пароль к пользовательской записи на linux системе, имя пользователя я знал, а вот пароль надо было восстановить старый. Хоть я и имел доступ с правами root'а и мог просто изменить пароль на данного пользователя, но мне стало интересно как можно этот пароль восстановить. После поисков и Интернете и на различных форумах я наткнулся на программку John the Ripper и решил воспользоваться ей.


Итак, согласно википедии:

John The Ripper (JTR)— свободная программа, предназначенная для восстановления паролей по их хешам. Основное назначение программы — аудит слабых паролей в UNIX системах. Программа также может выполнять аудит NTLM хешей, Kerberos, и др. Существуют реализации под различные ОС. Весьма популярна из-за поддержки большого количества хешей, автораспознавания хеша и настраиваемого взломщика. Также поддерживает модули, предоставляющие поддержку MD4 хешей, LDAP и MySQL паролей.


Однако, прежде чем начать разбираться с самим JTR, давайте остановимся на самих паролях в *nix системах.



Немного о паролях.



В Linux пароли шифруются в алгоритме «DES», также есть такая фишка как «salt», это 2е любые буквы, которые являются ключом к паролю, другими словами, с помощью «salt» и шифруется пароль. Подробнее об этом написано в статье «пароли в UNIXе».
Пароли в большинстве случаев хранятся в следующих файлах:



/etc/passwd
/etc/shadow


Хотя могут и лежать в каком-нибудь /etc/shadow.old или /etc/passwd.tmp. Сделав листинг директории /etc/, сразу можно все понять. Просто иногда хитрые админы меняют названия файлов :). passwd файл, это как раз то что нам надо, но в большинстве случаев пароли хранятся в shadow (в тени), в этом случае в passwd видим следующую ситуацию:



root:*:0:0:System Administrator:/root:/bin/csh
rfindd:*:66:1:Rfind Daemon and Fsdump:/var/rfindd:/bin/sh


Внимание! Это на самом деле fake, и никаких паролей здесь нет! А вот если видим такую ситуацию:



root:9IDv/CqdFuqWo:0:0:System Administrator:/root:/bin/csh
john:653MWdpUGN3BM:66:1:John Nikolsen, 2-nd west:/home/john:/bin/sh


То можно заметить, что данный пример более похож на правду. Давайте разберемся на примере с пользователем «john»:



john:653MWdpUGN3BM:66:1:John Nikolsen, 2-nd west:/home/john:/bin/sh
  1. john — имя пользователя.
  2. 653MWdpUGN3BM — пароль зашифрованный в DES.
  3. 66:1 — номер пользователя в системе: номер рабочей группы (у root всегда 0:0).
  4. John Nikolsen, 2-nd west — информация о пользователе (обычно Имя, Фамилия…).
  5. /home/john — Домашний каталог.
  6. /bin/csh — shell.

Во Free BSD пароли хранятся в файле /etc/master.passwd, а в остальном технология такая же как и в LINUX, хотя используется более стойкий алгоритм шифрования MD5. В Open BSD, для шифрования используется алгоритм Blowfish.


Ну а теперь о самом JTR и как им пользоваться.



Режимы.



JTR может работать в следующих режимах:

  1. Brute force
  2. Перебор по словарю (wordlist mode)
  3. Incremental mode
  4. Single mode (одиночный режим)


Рассмотрим каждый из режимов подробно.



Brute force:

john passwd

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

Перебор по словарю (wordlist mode):

john -w:wordlist passwd

где wordfile — имя файла со словарем. Также можно и так:

john -w:wordlist -rules passwd

-rules, включает правила синтаксиса для словаря. С вариантом по словарю, пароли подбираются на много быстрее чем в варианте с «Brute force».

Incremental mode:

john -i passwd

Авторы JTR говорят, что это самый сильный способ перебора, 95 символов. А вот таким способом:

john -i:alpha passwd

JTR перепробует 26 символов, от «a», до «zzzzzzzz». Если с wordlist не получилось, советую запускать данный способ.

Single mode (одиночный режим):

john -single passwd

Рекомендуется начать перебирать пароль с данной опцией. Намного быстрее чем wordlist mode, но рассчитан на хиленькие пароли. При переборе нескольких файлов с данной опцией, возможно подобрать пароли быстрее чем просто 1 passwd файл

Опции.



Теперь давайте рассмотрим несколько полезных опции для JTR. Опции можно запускать в любых режимах:-w, -i, -single.

Перебор для конкретного пользователя:

john -w:wordlist -rules -users:0 passwd

Перебирает пароль к конкретному пользователю (в нашем случае root). Что бы указать пользователя, надо после ":" указать UID (номер пользователя в системе).

Смотрим подобранные пароли.

john -show passwd

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

Разбиваем файл на куски.

john -w:wordlist -salts:5 passwd
john -w:wordlist -salts:-5 passwd


Иногда бывает полезно (если есть 2 компьютера). Число «5» это количество «salts» (паролей). JTR будет отчитывать с самого верху. В первом примере (на 1ом компе), берется первые 5 пользователей, во втором примере "-5" (на 2ом компе), перебираются все, кроме 5и первых.

Люблю shells.

john -w:wordlist -rules -shells:sh,csh,tcsh,bash passwd

В данном случае, будут перебираться только те пользователи, которые имеют указанные в вышеприведенном примере shell'ы(оболочки).

Несколько passwd файлов.
Если у вас есть несколько файлов с паролями в DES, то их можно всех вместе поставить на расшифровку данным способом:

john -single passwd passwd1 passwd2

Количество файлов по-моему не ограничено :). Способ с несколькими файлами, работает на всех режимах и опциях, просто укажите имена файлов в конце (как в примере).
Unshadow files.

unshadow passwd shadow

Если у вас пароли в shadow, то данной командой, их можно восстановить (требует shadow файл).

Ну вот вроде и все. В данной статье были описаны практически все самые полезные примеры, которым можно найти применение. За более подробной информацией можно обратиться к документации прилагаемой к JTR.

Ubuntu+Postfix как антиспам шлюз для Exchange2003

Ubuntu 8.10 (Postfix) в качестве почтового шлюза для Exchange+antispam
Как все начиналось или Зе хистори оф батл! ;)
В жизни каждого системного администратора наступает такой момент, когда количество спама, переходит все мыслимые и не мыслимые пределы. Когда начальство при вашем появлении в кабинете, начинает нудить — что устало начинать свой рабочий день с разгребания спама, вместо того чтобы совершать подвиги — типа разгона облаков или мативирования подчиненных на новые трудовые свершения — на ниве капиталистического труда.)
При этом денег на покупку лицензий антиспама выделять не хочет, мотивируя это тем, что: ТЫ и так умный, вот и придумай что-нибудь, да и кризис на дворе. А лицензирование большинства современных систем фильтрации нежелательной корреспонденции, основывается на на принципе:1 почтовый ящик =1 лицензия, а если количество ящиков больше 100, а если 200 и более как у меня, то сумма получается вполне приличной.

Схема работы системы будет следующей
Канал интернета ->шлюз ubuntu->сервер win2k3(exchange 2003, AD, DNS,WINS,ISA)ну и прочие прелести от мелко-мягких.
Установку Ubuntu server 8.10 оставлю за рамками статьи
Схема работы шлюза подробнее:
1) Ubuntu принимает, входящие письма от спамеров клиентов, проверяет IP отправителя, на наличие в списках негодяев (спам листах) – если IP присутствует, соединение разрывается, если отсутствует – считаем, отправителю повезло, переходим к пункту 2
2) Поверяем как представится сервер отправитель, если в helo содержит записи типа localhost или IP адрес отправитель идет лесом, а если сервер представился правильно проводим проверку по реверс ДНС запросу – если ответ не соответствует helo при начале соединения – отправитель посылается в пешее эротическое путешествие. Вот такая вот викторина получается, ах да, совсем забыл — если во время всей этой игры в кошки-мышки сервер отправитель допустил более 1й ошибки- то что? Правильно — соединение разрывается! Но если и тут все хорошо- то отправитель признается, самым хитрым спамером и я как Якубович предлагаю сыграть со мной в супер-игру, от которой нельзя отказаться с суперпризом будет велосипед, который мы только что изобрели, т.е. к переходим пункту 3.
3) .(СУПЕР игра и супер приз велосипед!) проверяем наличие почтовых ящиков получателей в Exhange, если получатель отсутствует в списке, то отправитель уходит несолоно хлебавши, со следами пинка на пятой точке и клеймом спамера-неудачника, соединение разрывается, не доходя то закачки тела сообщения ( экономия на трафике на лицо) — если присутствует то отправитель признается абсолютным чемпионом и письмо передается на exchange, который тихо сидит backend и его не видно, снаружи.

Переходим непосредственно к первой части нашего эротического боевика.
Предполагаю что система, уже установлена и обновления поставлены, настройки сделаны.
Заходим в качестве супер-пользователя
#Sudo su
Вводим пароль администратора
Ставим postfix

apt-get install postfix

Ну и далее запускаем текстовый редактор.
Вместо «сервер конторы.ru» подставляем реальные данные ВАШЕГО домена.
Начнем:
nano /etc/postfix/main.cf

нас интересуют следующие строки
myhostname = mail.сервер конторы.ru # полное имя хоста
mydomain = сервер конторы.ru # имя домена от имени которого приходит почта

mydestination = mail. сервер конторы.ru, localhost. сервер конторы.ru,, localhost
mynetworks = 127.0.0.0/8, 192.168.200.20 [::ffff:127.0.0.0]/104 [::1]/128 # список доверенных сетей из которых разрешена неавторизированная отправка, сервер win2k3 имеет адрес 192.168.200.20( это IP не я придумал, пришел на новую работу так все уже было настроено, ну а я систему ломать не стал), этой директивой мы разрешаем отправку почты наружу через postfix только непосредственно серверу с Exchange на борту, даже клиенты локальной сети должны отправлять почту только через Exhcange.

virtual_mailbox_domains = сервер конторы.ru

transport_maps = hash:/etc/postfix/transport
virtual_transport = hash:/etc/postfix/transport
Эти пункты отвечают за то, куда будет передаваться принятая почта.

В конце конфига добавляем

bounce_queue_lifetime = 1d
maximal_queue_lifetime = 1d
minimal_backoff_time = 180s
maximal_backoff_time = 12h

strict_rfc821_envelopes = yes
disable_vrfy_command = yes
smtpd_delay_reject = yes
smtpd_helo_required = yes
anvil_rate_time_unit = 60s
smtp_always_send_ehlo = yes
smtpd_hard_error_limit = 1
smtpd_recipient_limit = 1
smtpd_sasl_security_options = noanonymous

anvil_rate_time_unit = 60s
smtpd_client_connection_count_limit = 5
smtpd_client_connection_rate_limit = 6
smtpd_client_message_rate_limit = 6
smtpd_client_recipient_rate_limit = 1

smtpd_client_restrictions =
reject_unauth_pipelining,
permit_sasl_authenticated,
permit_mynetworks,
check_helo_access regexp:/etc/postfix/helo,
reject_unknown_client_hostname,
check_client_access regexp:/etc/postfix/dul_checks,
permit

smtpd_helo_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unknown_client,
# проверяем IP на присутствие в спам листах
reject_rbl_client cbl.abuseat.org,
reject_rbl_client sbl-xbl.spamhaus.org,
reject_rbl_client sbl.spamhaus.org,
reject_rbl_client dnsbl.njabl.org,
check_helo_access regexp:/etc/postfix/helo,
reject_invalid_helo_hostname,
reject_non_fqdn_helo_hostname,
reject_unknown_helo_hostname,
check_sender_access hash:/etc/postfix/access,
check_recipient_access hash:/etc/postfix/recipients,
permit

smtpd_sender_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_non_fqdn_sender,
reject_unknown_sender_domain,
permit

smtpd_recipient_restrictions =
reject_unauth_pipelining,
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination,
reject_invalid_hostname,
reject_non_fqdn_recipient,
reject_unknown_recipient_domain,
permit

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

Нам необходимо создать следующие файлы:
nano /etc/postfix/transport # отвечает за то, куда нужно передавать принятую почту, содержт:

серверконторы.ru smtp:[192.168.200.20]

где: smtp-серер получатель, IP в [ ] говорит о том, что не нужно проверять его ДНС зону, а передавать почту на этот адрес как есть, а там разберутся!

nano /etc/postfix/recipients
Это наш список получателей, который экспортируется из Exchange
Содержит записи вида:

user@серверконторы.ru OK

где: user-имя пользователя в AD c mailbox, серверконторы.ru-домен получателя, OK –директива, как поступить с письмом, в нашем случае – пропустить.
Сведения о получателях будут содержаться именно в этом файле, можно конечно сделать опрос exchange в реальном времени, но тут появляется ненужная нагрузка на контроллер домена, а оно вам надо?! Причем если одновременно долбиться несколько сот тыс. отправителей все эти запросы посыпятся и на AD. Это и потерянное время на ожидание ответа от винды и нагрузка на процессор уже 2х серверов. По моему глубокому мнению, проще запустить скрит через, который в заданное время, выдернет список получателей, и перебросит их в файл, как часто его запускать — решать вам, у меня он запускается 2 раза в день 12 и 15 часов — больше не требуется. А ночью, мы, слава богу, не работаем.

nano /etc/postfix/helo
содержит:
/([0-9]{1,3}(\.|-)){3}[0-9]{1,3}/i REJECT IP-able helo SPAM!
/localhost/i REJECT you are SPAM!
/ сервер конторы.ru/i REJECT you are not in my local networks-SPAM!

В этом файле используются регулярные выражения для фильтрации команды helo
Если отправитель представился IP адресом-то это нарушение и почту от него не принимают, тоже самое со вторым пунктом localhost –нормальный сервер не может представляться так, либо там админ криворукий (пускай настраивает нормально) либо на другом конце просто сильно запаршивевший компьютер, который является частью ботнета, рассылаешего спам. Ну и последнее если отправитель представляется — серверконторы.ru, ну не может на другом конце быть мой сервер — где же тогда я нахожусь, это на 100% спамер можно посылать смело. Команда REJECT указывает на то, что сделать при выполнении условий.
Да ксати после идет ответ сервера, с указанием причины отказа в принятии почты, туда можно вписать что-то обидное ;) и это сообщение увидят на том конце. Но это дело воспитания конкретного индивидуума.

nano /etc/postfix/access
содержание данного файла схоже с предыдущим, но тут уже используются явные указатели, этим файлом можно, например, блокировать отдельные ящики пользователей.
bad_user@сервер конторы.ru REJECT user is note available
сервер конторы.ru REJECT you are not in my local networks
IP вашего сервера REJECT you are not in my local networks
localhost REJECT you are SPAM!

nano /etc/postfix/dul_checks
проверка на наличии в различных сетях
содержит регулярные выражения различных сетей и как следствие признаков что это не почтовые сервера а зараженные клиентские компьютеры, а если там действительно работают нормальные сервера, то почту можно отправлять и через релей провайдера или на худой конец обратиться в тех поддержку чтобы прописали обратную DNS зону.

/([0-9]*-){3}[0-9]*(\..*){2,}/i REJECT 553 SPAM_ip-add-rr-ess_networks
/([0-9]*\.){4}(.*\.){3,}.*/i REJECT 553 SPAM_ip-add-rr-ess_networks
/.*\.broadband\.hu/i REJECT 553 SPAM_broadband-hu
/client.*\..*\..*/i REJECT 553 SPAM_CLIENT
/cable.*\..*\..*/i REJECT 553 SPAM_CABLE
/pool.*\..*\..*/i REJECT 553 SPAM_POOL
/dial.*\..*\..*/i REJECT 553 SPAM_DIAL
/ppp.*\..*\..*/i REJECT 553 SPAM_PPP
/dslam.*\..*\..*/i REJECT 553 SPAM_DSLAM
/dhcp.*\..*\..*/i REJECT 553 SPAM_DHCP
/[\.-]dsl.*\..*\..*/i REJECT 553 SPAM_DSL
/[ax]dsl.*\..*\..*/i REJECT 553 SPAM_XDSL
/.*([0-9]*\.){4}cableonline\.com\.mx/i REJECT 553 SPAM_IP-cableonline-com-mx
/.*\.([0-9]*\.){4}ip\.holtonks\.net/i REJECT 553 SPAM_ip-holtonks-net
/([0-9]*-){3}[0-9]*\.fibertel\.com\.ar/i REJECT 553 SPAM_IP-fibertel-com-ar
/.*[0-9]*-[0-9]*\.fibertel\.com\.ar/i REJECT 553 SPAM_IP-fibertel-com-ar
/[0-9]*\.user\.veloxzone\.com\.br/i REJECT 553 SPAM_user-veloxzone-com-br
/[0-9]*\.customer\.alfanett\.no/i REJECT 553 SPAM_customer-alfanett-no
/.*([0-9]*-){3}[0-9]*\.telecom\.net\.ar/i REJECT 553 SPAM_host-telecom-net-ar
/.*(-[0-9]*){2}\.telpol\.net\.pl/i REJECT 553 SPAM_host-telpol-net-pl
/(.*\.){2}maxonline\.com\.sg/i REJECT 553 SPAM_host-maxonline-com-sg
/(.*-){2}.*\.fairgamemail\.us/i REJECT 553 SPAM_host-fairgamemail-us
/[0-9]*[0-9]*-\.wispnet\.net/i REJECT 553 SPAM_host-wispnet-net
/.*-.*(\..*){2}\.ne\.jp/i REJECT 553 SPAM_host-ne-jp
/[0-9]*\..*\.ne\.jp/i REJECT 553 SPAM_h09t-ne-jp
/(.*\.){3}ad\.jp/i REJECT 553 SPAM_host-ad-jp
/(.*\.){4}revip\.asianet\.co\.th/i REJECT 553 SPAM_revip-asianet-co-th
/[0-9]*\..*\.virtua\.com\.br/i REJECT 553 SPAM_host-virtua-com-br
/([0-9]*-){3}[0-9]*\.exatt\.net/i REJECT 553 SPAM_host-exatt-net
/([0-9]*\.){4}ip\.alltel\.net/i REJECT 553 SPAM_host-ip-alltel-net
/[0-9]{6,}\.chello\.../i REJECT 553 SPAM_host-chello
/.*[0-9]*\..*\.chello\.../i REJECT 553 SPAM_host-chello-xx
/.*\..*\.t-dialin\.net/i REJECT 553 SPAM_t-dialin-net
/.*\..*\.t-ipconnect\.de/i REJECT 553 SPAM_t-ipconnect-de
/([0-9]*-){2,3}[0-9]*\..*\.cgocable\.net/i REJECT 553 SPAM_host-cgocable-net
/.*\..*\.shawcable\.net/i REJECT 553 SPAM_host-shawcable-net
/p[0-9]*\.mp[0-9]*\.aaanet\.ru/i REJECT 553 SPAM_aaa_modem_pool
/([0-9]*-){2}[0-9]*\.ip\.adsl\.hu/i REJECT 553 SPAM_ip-adsl-hu
/([0-9]{1,3}\.){2}broadband4\.iol\.cz/i REJECT 553 SPAM_broadband-iol-cz

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

postmap /etc/postfix/transport
postmap /etc/postfix/recipients
postmap /etc/postfix/helo
postmap /etc/postfix/access
postmap /etc/postfix/dul_checks

вот пожалуй и все с ubuntu

Чась вторая: Настройка Win2k3

По моему глубокому мнению, не имеет смысла пускать сервер с передовой, в тыл нашей обороны, по этой же причине мы не будем давать лазить в системе и опрашивать ее, мы разрешим ей видеть только то, что мы сами разрешили.
Как это можно реализовать на практике?! Postfix будет видеть только тот список получателей который мы ему сами дадим -уже готовый!
В то время когда я только собирался все это безобразие настраивать-у меня был скрипт, на перле, который опрашивает AD, но с ним что-то не заладилось, во-первых там была какае-то шляпа с ключами, да и пересобирать openLDAP не очень хотелось.
Я пошел иным путем, на самом сервере win 2003 через планировщик заданий мы будем запускать скрипт который будет сбрасывать нам список пользователей в файл recipients.

Схема работы системы Win:
В AD заводится пользователь с минимальными правами, без почтового ящика, с правом доступа, лишь в одну папку. Назовем его postfix с паролем 1234567.
Через планировщик заданий создается новое задание с частотой выполнения нужной вам.
На просторах сети был найден скрипт, который не полностью соответствовал нашим задачам, я его слегка допилил и теперь он делает на 100% то что нужно.
Сам скрипт написан на VBS, назовем его mail.vbs-такого содержания:

Option Explicit

Dim StartTime,EndTime: StartTime = Now ' For seeing how long the script takes to run
Dim objShell
Dim objFSO
Const ScriptVersion = «1.01»
Set objShell = WScript.CreateObject(«WScript.Shell»)
Set objFSO = CreateObject(«Scripting.FileSystemObject»)
Wscript.Echo «StartTime = » & StartTime
' ***************************************************************** '
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Dim objRootDSE
Dim objDomain
Dim objContainer
Dim objOrganizationalUnit
Dim strOutputFileName, objOutputFileName, GarbageRC
Dim intUserObjectCountAll, intUserObjectCountSelected

strOutputFileName = «c:\путь\ к расшаренной папке\ для пользователя postfix\recipients»

Set objOutputFileName = objFSO.OpenTextFile(strOutputFileName, ForWriting, True)
intUserObjectCountAll = 0
intUserObjectCountSelected = 0

Set objRootDSE = GetObject(«LDAP://RootDSE»)
Set objDomain = GetObject(«LDAP://» & objRootDSE.Get(«DefaultNamingContext»))

Call Sub_EnumOUs(objDomain.ADsPath)

Sub Sub_EnumOUs(sADsPath)
Set objContainer = GetObject(sADsPath)
objContainer.Filter = Array(«OrganizationalUnit»)
For Each objOrganizationalUnit in objContainer
WScript.Echo «Checking OU: » & objOrganizationalUnit.ADsPath
Wscript.Echo " User Object Count: " & intUserObjectCountAll
Sub_EnumUsers(objOrganizationalUnit.ADsPath)
Sub_EnumOUs(objOrganizationalUnit.ADsPath)
Next
End Sub

Sub Sub_EnumUsers(sADsPath)
Dim objADobject
Set objContainer = GetObject(sADsPath)
objContainer.Filter = Array(«User»)
For Each objADobject in objContainer
If objADobject.Class = «user» Then
intUserObjectCountAll = intUserObjectCountAll + 1
If objADobject.Mail <> "" Then
objOutputFileName.Writeline( objADobject.Mail & " OK" ) ' *** "[TAB]OK"- разделение с помощью табуляции **** '
intUserObjectCountSelected = intUserObjectCountSelected + 1
End If
End If
Next
End Sub

objOutputFileName.Close

' ***************************************************************** '

Чтобы создать задание в планировщике, в строку инициализации нашего скрипта пишем:
c:\windows\system32\cscript.exe c:\путь к папке со скриптом\mail.vbs
ВСЕ это одной строкой!
Ну и выставляем время, когда он будет выполнятся, и как часто.
Далее создаем директорию и открываем в нее доступ по сети пользователю postfix.

Часть треться –заключительная.
Теперь задача, чтобы наш linux сервер мог увидеть содержимое папки с файлом recipirnts
Я предпочел монтировать нашаренную директорию при старте системы, по этому, запихал команду в файл монтирования /etc/rc.local.
Также необходимо создать в директории media директорию win. Почему именно win- чтобы через несколько месяцев, можно было понять, что это такое и откуда оно взялось.
Сначала ставим пакет smbfs -без него наша директория не примантируется.
Sudo apt-get install smbfs
Sudo nano /etc/rc.local
И перед строкой exit0 вписываем команду монтирования шары:
mount -t smbfs //192.168.200.20/antispam /media/win/ -o iocharset=utf8,user=postfix,pass=1234567

где: 192.168.200.20-ip сервера с exchange.
antispam-директория в которой лежит файл recipients.
/media/win/-куда должна примантироваться виндовая шара.
iocharset=utf8-кодировака, если случайно в ней появятся папки с русскими названиями то они будут читаемы, а не ???????????, да и хуже от этого не будет, в общем делаем сразу как надо и спим спокойно.
user=postfix,pass=1234567 –имя пользователя и пароль для доступа к шаре.

Перезагружаем Ubuntu лезем в /media/win/ видим ее содержимое виндовой шары- значит все отлично.

Далее создадим небольшой скрипт который будет переписывать файл recipients
В директорию /etc/postfix затем будет пере конвертировать его в базу данных postfix
И перезагружать почтовик.

#!/bin/bash
cp /media/win/recipients /etc/postfix/
postmap /etc/postfix/recipients
/etc/init.d/postfix restart

Создаем задание в CRON которое будет выполняться через 3 мин после выполнения задания на Win2k3-на всякий случай

Вот и все!

Что мы получили
Аниспам фильтр, с высокой степенью эффективности-до его установки я каждое утро разгребал по 240-260 писем сейчас наблюдаю только нужную почту.
Единственный ящик в который спам, все таки доходит, это ящик на который приходят заявки для нашей конторы, но их число не превышает 3-4.
Безопасность — в случае компрометации сервера Linux шлюз легко восстанавливается,
Да и злоумышленник не получит доступа к корреспонденции, т.к ее на нем просто нет, она сразу передается Exchange-если проходит все проверки.
Кросс платформенность-нет смысла собирать модули с поддержкой всяких экзотических функций.

Время на развертывание-1 час с условием что Интернет шустрый, да и если все копипастить как есть — система протестирована в течении 3х мес. -работает без сбоев.
А теперь, как говорят в Одессе -Подабьем бабки…
Стоимость шлюза 14000руб. –можно и дешевле.
Стоимость антиспама от “Кашпировского” по минимуму 34500, по хорошему 76000-это так, на вскидку.
Вычитаем затраты на железо = сальдо в нашу пользу! Можно у руководства просить премию…
А что еще можно прикрутить к нему- решать вам.

Да немного аналитики-за время использования данной схемы читал логи, так вот-в первые 2 недели работы, очень много писем отбрасывалось по IP заблокированных спам-листами.
Теперь в основном по helo -т.е используются в основном IP не попавшие в спам-листы, очень удивило что на той стороне силы, все анализируется и проверяется… Много думал!
Кстати нужно будет протестировать на Win2k8 и Exchange2k7 по всем вариантам должно пахать нормально.

BackUp DataBase для маленького ресурса

Скрипт будет выдавать ошибку времени исполнения при большом объеме базы. Во избежание этого нужно рекурсивно вызывать скрипт, но еще не придумал как.
Если укажите на ошибки буду только рад. Скрипт естественно несовершенен.

<?php

$tmp_backup_cfg = "backup_config.dat";
$backup_cfg = "backup_cfg.php";

$parse = "////\n";

chdir(dirname($_SERVER['PHP_SELF'])."/");

function structuredb(){
require_once("backup_cnt.php");

$struct_db = "structure_".$backup_dbname.".dat"; // файл структуры БД
if (file_exists($struct_db)) unlink($struct_db);
$fp = fopen($struct_db,"a+");

$tables = mysql_list_tables($backup_dbname); // список таблиц
while ($table = mysql_fetch_row($tables)) { // перебор таблиц

//if ($table[0] != "rusfusion_aw_ec_logins") {

$fields = mysql_list_fields($backup_dbname,$table[0]); // список полей таблицы
$count_fields = mysql_num_fields($fields); // количество полей

fwrite($fp,"\nTable: ".$table[0]."\n");
for ($i = 0; $i < $count_fields; $i++){ // перебор полей
$name_fields = mysql_field_name($fields, $i); // создание массива с именами полей для дальнейшей выборки из бд
$type_fields = mysql_field_type($fields, $i);
$len_fields = mysql_field_len($fields, $i);
$flag_fields = mysql_field_flags($fields, $i);
$name_field = mysql_field_name($fields, $i); // вывод шапки таблицы (имена полей)
fwrite($fp,"Field: $name_fields | $type_fields | $len_fields | $flag_fields\n");
} // END FOR #1
//}//ENDIF
} // END WHILE #1
fclose($fp);

$content = "Save structure db $backup_dbname - complite.";
$fo = fopen($struct_db,"r");
$content .= "

Структура БД: $backup_dbname



";

$data = fread($fo,filesize($struct_db));
$data = str_replace("\n","
",$data);
$data = str_replace("Table:","Table:",$data);
$data = str_replace("Field:","    Field:",$data);

fclose($fo);
$content .= $data;

$content .= "

<form method=\"POST\" action='".$_SERVER['PHP_SELF']."'>
";

return $content; //"Function Structure DB";
}

function copydb($parse,$start){
require_once("backup_cnt.php");
$content = "";
$dirdata = "database";
if (!file_exists($dirdata) || !is_dir($dirdata)) mkdir($dirdata);
$tables = mysql_list_tables($backup_dbname); // список таблиц
while ($table = mysql_fetch_row($tables)){ // перебор таблиц

$fields = mysql_list_fields($backup_dbname,$table[0]); // список полей таблицы
$count_fields = mysql_num_fields($fields); // количество полей

$name_fields = "";
for ($i = 0; $i < $count_fields; $i++){ // перебор полей
$name_fields[] .= mysql_field_name($fields, $i); // создание массива с именами полей для дальнейшей выборки из бд
} // END FOR #1

$sql_count = mysql_query("SELECT count(*) AS c FROM ".$table[0]);
$count_str = mysql_result($sql_count,'c');
/**Нужно рекурсивно вызывать скрипт с заданным началом отсчета. А вход в функцию по переменной в адресе!**/
if ($count_str > 500){
$count = 500;
if (empty($start)) $start = 0; else $start = intval($start);
$file = $table[0].".dat";
if (file_exists($dirdata."/".$file)) unlink($dirdata."/".$file);
$fo = fopen($dirdata."/".$file,"a");
if ($count_str > $start+$count){
$sql = mysql_query("SELECT * FROM ".$table[0]." LIMIT $start, $count"); // выборка
$i = 0;
while (mysql_fetch_array($sql)){
fputs($fo,$parse);
for ($y = 0; $y < $count_fields; $y++){
$name_field_db = $name_fields[$y];
$data_field = mysql_result($sql,$i,$name_field_db);
$dstr = $data_field."\n";
//$dstr = "[".$name_field_db."] => ".$data_field."\n";
fputs($fo,"$dstr");
} // END FOR #2
fputs($fo,"\n\n");
$i++;
} // END WHILE #2
$start =+ $count;
//header("Location: backup.php?s=$start");
//die();
} // END WHILE #1
if ($count_str > $start){ // Копируем остаток таблицы
$count = $count_str - $start;
$sql = mysql_query("SELECT * FROM ".$table[0]." LIMIT $start, $count"); // выборка
$i = 0;
while (mysql_fetch_array($sql)){
fputs($fo,$parse);
for ($y = 0; $y < $count_fields; $y++){
$name_field_db = $name_fields[$y];
$data_field = mysql_result($sql,$i,$name_field_db);
$dstr = $data_field."\n";
//$dstr = "[".$name_field_db."] => ".$data_field."\n";
fputs($fo,"$dstr");
} // END FOR #2
fputs($fo,"\n\n");
$i++;
} // END WHILE #2
}
fclose($fo);
$content .= "Table ".$table[0]." ($count_str) - complite.<br>";
} else {
$sql = mysql_query("SELECT * FROM ".$table[0]); // выборка
$file = $table[0].".dat";
if (file_exists($dirdata."/".$file)) unlink($dirdata."/".$file);
$fo = fopen($dirdata."/".$file,"a");
$i = 0;
while (mysql_fetch_array($sql)){
fputs($fo,$parse);
for ($y = 0; $y < $count_fields; $y++){
$name_field_db = $name_fields[$y];
$data_field = mysql_result($sql,$i,$name_field_db);
$dstr = $data_field."\n";
//$dstr = "[".$name_field_db."] => ".$data_field."\n";
fputs($fo,"$dstr");
} // END FOR #2
fputs($fo,"\n\n");
$i++;
} // END WHILE #2
fclose($fo);
$content .= "Table ".$table[0]." ($i) - complite.<br>";
} // if >5000
} // END WHILE #1

global $backup_cfg;
unlink($backup_cfg);
return $content;
}

function sendmail(){
$boundary = "--".md5(uniqid(time()));
$headers = "From: My site<noreply@example.com>\r\n";
$headers .= "Reply-To: mail@example.com\r\n";
$headers .= "Return-Path: mail@example.com\r\n";
$headers .= "X-Mailer: Example\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/mixed; boundary=\"$boundary\"\r\n";
$kod = 'windows-1251';
$multipart .= "--$boundary\n";
$multipart .= "Content-Type: text/html; charset=$kod\n";
$multipart .= "Content-Transfer-Encoding: Quot-Printed\n\n";
$multipart .= "Резервная копия\n\n";

global $backup_dbname;
$path = "structure_".$backup_dbname.".dat";
if (file_exists($path)){
$fp = fopen($path,"r");
if ($fp){
$file = fread($fp,filesize($path));
fclose($fp);
$message_part = "";
$message_part .= "Content-Type: application/octet-stream";
$message_part .= "; name = \"$path\"\n";
$message_part .= "Content-Transfer-Encoding: base64\n";
$message_part .= "Content-Disposition: attachment; filename = \"".$path."\"\n\n";
$message_part .= chunk_split(base64_encode($file))."\n\n";
$multipart .= "--$boundary\n".$message_part;
unlink($path);
}
}

$database = opendir("database/");
while ($filename = readdir($database)){
$ftype = filetype("database/".$filename);
if ($ftype != "dir" && preg_match("#.dat$#D",$filename)){
$path = "database/".$filename;
$fp = fopen($path,"r");
if ($fp){
if (filesize($path) == 0) $file = "";
else $file = fread($fp,filesize($path));
fclose($fp);
$message_part = "Content-Type: application/octet-stream";
$message_part .= "; name = \"$filename\"\n";
$message_part .= "Content-Transfer-Encoding: base64\n";
$message_part .= "Content-Disposition: attachment; filename = \"".$path."\"\n\n";
$message_part .= chunk_split(base64_encode($file))."\n\n";
$multipart .= "--$boundary\n".$message_part;
unlink($path);
} // END IF #2
}
} // END WHILE
$multipart .= "--$boundary--\n";

if (mail("mail@example.com","BackUp",$multipart,$headers))
return "Сообщение отправлено.";
else return "Ошибка отправки.";
}

$content = "

Резервное копирование

";

if (file_exists($backup_cfg)) require_once($backup_cfg);

/**** Не работает ****/
if (isset($_GET['s']) && !empty($_GET['s'])){
$content .= copydb($parse,$_GET['s']);
}

if (isset($_POST['nextstep'])){
$content .= copydb($parse,"");
$content .= sendmail();
//chmod(BASEDIR."backup",0644);
} elseif (isset($_POST['deleteconfig'])){
unlink($backup_cfg);
header("Refresh: 0;");
} elseif (isset($_POST['servername']) && isset($_POST['inputserver']) && !empty($_POST['servername'])){
$backup_fp = fopen($tmp_backup_cfg,"w");
$backup_server = $_POST['servername'];
fwrite($backup_fp,$backup_server);
fclose($backup_fp);
header("Refresh: 0;");
} elseif (file_exists($tmp_backup_cfg)){
if (isset($_POST['logindb']) && isset($_POST['passdb']) && isset($_POST['inputauth']) && !empty($_POST['logindb'])){
$backup_fp = fopen($tmp_backup_cfg,"a");
$backup_lp = $_POST['logindb']."\n".$_POST['passdb'];
fwrite($backup_fp,"\n".$backup_lp);
fclose($backup_fp);
header("Refresh: 0;");
} elseif (isset($_POST['namedb']) && isset($_POST['selectdb']) && !empty($_POST['namedb'])){
$backup_fp = fopen($tmp_backup_cfg,"a");
fwrite($backup_fp,"\n".$_POST['namedb']);
fclose($backup_fp);
$backup_fp = fopen($tmp_backup_cfg,"r");
$backup_data = fread($backup_fp,filesize($tmp_backup_cfg));
fclose($backup_fp);
$backup_str = explode("\n",$backup_data);
$backup_servername = $backup_str[0];
$backup_login = $backup_str[1];
$backup_password = $backup_str[2];
$backup_dbname = $backup_str[3];
$backup_fp = fopen($backup_cfg,"w");
$backup_write = '<?php'."\n".'$backup_servername = "'.$backup_servername.'";'."\n".'$backup_login = "'.$backup_login.'";'."\n".'$backup_password = "'.$backup_password.'";'."\n".'$backup_dbname = "'.$backup_dbname.'";'."\n".'?>';
fwrite($backup_fp,$backup_write);
fclose($backup_fp);
unlink($tmp_backup_cfg);
if (isset($_POST['struct'])) $content .= structuredb();
else {
$content .= copydb($parse,"");
$content .= sendmail();
//chmod(BASEDIR."backup",0644);
}

} else {
$backup_fp = fopen($tmp_backup_cfg,"r");
$backup_data = fread($backup_fp,filesize($tmp_backup_cfg));
fclose($backup_fp);
$backup_count_str = count(explode("\n",$backup_data));
if ($backup_count_str == 3){
$backup_data = explode("\n",$backup_data);
$backup_servername = $backup_data[0];
$backup_login = $backup_data[1];
$backup_password = $backup_data[2];
$backup_conn = @mysql_connect($backup_servername,$backup_login,$backup_password);
if (!$backup_conn){
unlink($tmp_backup_cfg);
header("Refresh: 5;");
die("Could not connect: Неверный логин или пароль. Страница перезагрузится автоматически через 5 секунд.");
}
$backup_dbs = @mysql_list_dbs($backup_conn);

$content .= "
<form method=\"POST\" action='".$_SERVER['PHP_SELF']."'>

База Данных для копирования:


--";

while ($backup_db_name = mysql_fetch_object($backup_dbs)){
$content .= "
<option value='".$backup_db_name->Database."'"; if(isset($backup_dbname) && $backup_db_name->Database == $backup_dbname) $content .= " selected"; $content .= ">".$backup_db_name->Database."";
}
$content .= "

Копировать структуру базы.

";

} elseif ($backup_count_str == 1){
$content .= "
<form method=\"POST\" action='".$_SERVER['PHP_SELF']."'>

Логин:

Пароль:


";
} elseif ($backup_count_str == 3){
$backup_fp = fopen($tmp_backup_cfg,"a");
fwrite($backup_fp,"\n".$_POST['namedb']);
fclose($backup_fp);
$backup_fp = fopen($tmp_backup_cfg,"r");
$backup_data = fread($backup_fp,filesize($tmp_backup_cfg));
fclose($backup_fp);
$backup_str = explode("\n",$backup_data);
$backup_servername = $backup_str[0];
$backup_login = $backup_str[1];
$backup_password = $backup_str[2];
$backup_dbname = $backup_str[3];
$backup_fp = fopen($backup_cfg,"w");
$backup_write = '<?php'."\n".'$backup_servername = "'.$backup_servername.'";'."\n".'$backup_login = "'.$backup_login.'";'."\n".'$backup_password = "'.$backup_password.'";'."\n".'$backup_dbname = "'.$backup_dbname.'";'."\n".'?>';
fwrite($backup_fp,$backup_write);
fclose($backup_fp);
unlink($tmp_backup_cfg);
if (isset($_POST['struct'])) $content .= structuredb();
else {
$content .= copydb($parse,"");
$content .= sendmail();
//chmod(BASEDIR."backup",0644);

}
} else {
unlink($tmp_backup_cfg);
if (file_exists($backup_cfg)) unlink($backup_cfg);
header("Refresh: 0;");
}

}
} else {
if (file_exists($backup_cfg)){
$content .= "

Найден файл конфигурации:

";
$content .= "Server Name: ".$backup_servername."<br>";
$content .= "Login: ".$backup_login."<br>";
$content .= "Password: ".$backup_password."<br>";
$content .= "DB Name: ".$backup_dbname."<br>";
$content .= "
<form method=\"POST\" action='".$_SERVER['PHP_SELF']."'>";
}
$content .= "
<form method=\"POST\" action='".$_SERVER['PHP_SELF']."'>

Имя сервера:


";
}
$content .= "";
print $content;
?>

Делаем свой Pager для ASP.NET Web Forms

Предыстория


Думаю, каждый кто хоть раз реализовал пэйджизацию на asp.net(не MVC) плевался и задавал простой вопрос. Неужели нельзя было сделать по-людски. К примеру, стандартный GridView генерирует ссылки вида
«javascript:__doPostBack('ctl00$ctl00$MainContent$LiveExample$GridView1','Page$2')»>. Думаю не нужно объяснять, почему это плохо. В версии 3.5 появился DataPager, в котором можно передать номер страницы через GET запрос. Но всеобщие счастье опять не настало Необходимо, чтобы контрол реализовал интерфейс IPageableItemContainer, а его реализует только ListView. Это как я понял новый сверхмощный контрол на замену DataList GridView. Соответственно если нужно использовать новый пэйджер со старыми контролами нужно реализовать интерфейс. Как это сделать можно прочитать к примеру тут. Основной минус данного подхода — при связывании данных будет извлекаться вся(если не использовать LINQ DataSource) коллекция и исходя из этого строится грид и пэйджер. Обычно при постраничном отображении мы имеем коллекцию объектов для отображения на текущей странице, плюс мы знаем общее число страниц и текущую страницу. Эти предпосылки плюс не желание в 3-раз (все же 2 раза я это сделал) писать пэйджер под каждый конкретный грид послужило поводом сделать что-то более общее.

Требования


  • Идентификатор страницы при пейджизации должен передаваться в строке GET запроса
  • Контролу должен знать количество страниц для отображения
  • визуальные элементы отображения (ссылки следующая, предыдущая, первая, последняя и тд) должны быть шаблонными, что бы при использовании можно было легко править стил пэйджера под текущую страницу
  • естественно свойства для имени параметра идентифицирующего номер странице в строке запроса

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

Реализация


При реализации возник вопрос, что делать чистый серверный контрол или User Control. Был выбран второй. Почему? ну так фишка легла. Всегда можно сделать по-другому.
Если кто не в курсе как писать шаблонные user контролы, то это хорошо описано тут, а еще лучше тут.

Для начала нужно создать класс для шаблона.
public class PageContainer : Control, INamingContainer
{
  private int pageIndex;

  private string destinationURL;

  public string DestinationURL
  {
    get
    {
      return destinationURL;
    }
  }

  public int PageIndex
  {
    get
    {
      return pageIndex;
    }
  }

  public PageContainer(int index,string url)
  {
    pageIndex = index;
    destinationURL = url;
  }
}


* This source code was highlighted with Source Code Highlighter
.



Далее создаем несколько шаблонов
private ITemplate currentPageTemplate; // текущая страница

  [TemplateContainer(typeof(PageContainer))]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  public ITemplate CurrentPageTemplate
  {
    get
    {
      return currentPageTemplate;
    }
    set
    {
      currentPageTemplate = value;
    }
  }

  private ITemplate nextPageTemplate; // следующая страница

  [TemplateContainer(typeof(PageContainer))]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  public ITemplate NextPageTemplate
  {
    get { return nextPageTemplate; }
    set { nextPageTemplate = value; }
  }

  private ITemplate prevPageTemplate; // предыдущая страница

  [TemplateContainer(typeof(PageContainer))]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  public ITemplate PrevPageTemplate
  {
    get { return prevPageTemplate; }
    set { prevPageTemplate = value; }
  }

  private ITemplate numericalPageTemplate; // шаблон для отображения номера страницы

  [TemplateContainer(typeof(PageContainer))]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  public ITemplate NumericalPageTemplate
  {
    get { return numericalPageTemplate; }
    set { numericalPageTemplate = value; }
  }

  private ITemplate firstPageTemplate; // первая страница

  [TemplateContainer(typeof(PageContainer))]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  public ITemplate FirstPageTemplate
  {
    get { return firstPageTemplate; }
    set { firstPageTemplate = value; }
  }

  private ITemplate lastPageTemplate; // последняя страница

  [TemplateContainer(typeof(PageContainer))]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  public ITemplate LastPageTemplate
  {
    get { return lastPageTemplate; }
    set { lastPageTemplate = value; }
  }


* This source code was highlighted with Source Code Highlighter
.



На странице отображатся будет в следующей последовательности
[PrevPageTemplate][FirstPageTemplate][NumericalPageTemplate][CurrentPageTemplate][NumericalPageTemplate][LastPageTemplate][NextPageTemplate]

Вся генерация происходить в методе
public override void DataBind()
  {

    if( CurrentPageIndex >0)
    {
      if(PrevPageTemplate!=null)
      {
        AddTemplateAsControl(PrevPageTemplate, new PageContainer(CurrentPageIndex-1,GetDestinationURL(CurrentPageIndex-1)));
      }
      if(FirstPageTemplate!=null)
      {
        AddTemplateAsControl(FirstPageTemplate, new PageContainer(0, GetDestinationURL(0)));
      }
    }
    for(int i=CurrentPageIndex-NumericRange;i<CurrentPageIndex; i++)
    {
      if(i>=0 && NumericalPageTemplate!=null)
      {
        AddTemplateAsControl(NumericalPageTemplate, new PageContainer(i,GetDestinationURL(i)));
      }
    }

    if(CurrentPageTemplate!=null)
    {
      AddTemplateAsControl(CurrentPageTemplate, new PageContainer(CurrentPageIndex, GetDestinationURL(CurrentPageIndex)));
    }
    for(int i=CurrentPageIndex+1;i<CurrentPageIndex+NumericRange; i++)
    {
      if(i<=TotalPageCount && NumericalPageTemplate!=null)
      {
        AddTemplateAsControl(NumericalPageTemplate, new PageContainer(i,GetDestinationURL(i)));
      }
    }
    if(CurrentPageIndex!=TotalPageCount )
    {

      if(LastPageTemplate!=null)
      {
        AddTemplateAsControl(LastPageTemplate, new PageContainer(TotalPageCount, GetDestinationURL(TotalPageCount)));
      }
      if(NextPageTemplate!=null)
      {
        AddTemplateAsControl(NextPageTemplate, new PageContainer(currentPageIndex+1, GetDestinationURL(currentPageIndex+1)));
      }

    }
    base.DataBind();
  }


* This source code was highlighted with Source Code Highlighter
.



А теперь просто добавляем контрол на любую страницу


 <uc:pager ID="super_pager" TotalPageCount="10" NumericRange="3" QueryStringParameter="p" runat="server" >
      <NumericalPageTemplate>
          <a href='<%# Container.DestinationURL %>'><%# Container.PageIndex %></a>      
      </NumericalPageTemplate>   
      <CurrentPageTemplate>
        <span style="background-color:Red; color:Black;">
         current page <b> <%# Container.PageIndex %></b>
        </span>      
      </CurrentPageTemplate>
      <FirstPageTemplate>
        <a href='<%# Container.DestinationURL %>'><b>First</b></a>
      </FirstPageTemplate>
      <LastPageTemplate>
       <a href='<%# Container.DestinationURL %>'><b>Last</b></a>
      </LastPageTemplate>
      <NextPageTemplate>
       <a href='<%# Container.DestinationURL %>'>--></a>      
      </NextPageTemplate>
      <PrevPageTemplate>
       <a href='<%# Container.DestinationURL %>'><--</a>
      </PrevPageTemplate>    
    </uc:pager>

* This source code was highlighted with Source Code Highlighter
.



Правя шаблон можно сделать пейджизацию digg or Habra style

Еще немного занудства
  • TotalPageCount количестьво страниц
  • NumericRange сколько страниц(слева и справа) будет отображатся от текущей в числомов шаблоне
  • QueryStringParameter параметр в котором хранится номер страницы


Исходный код можна скачать тут

Заключение


Если вы используете URL Rewriting, то придется вместо свойства контейнера DestinationURL формировать ссылку самому. Если кто-то как выполнить обратную операцию для URL Rewriting, то будет весьма полезно.
К слову сказать по аналогии можно реализовать систему табов(разделов) на сайте. Фактически вместо номера страницы будет использоваться идентификатор таба. Можно разместить на странице несколько таких контролов и организовать «cross paging», а скорее cross tabing. Например первый контрол отвечает за бренд продукции второй за категорию или один за период дат (месяц года или день) другой за тэг

Создание deb пакета без использования стандартных утилит Debian

В этой статье я рассужу как создать пакет для установки в Debian не используя стандартные утилиты из Debian, такие как devhelper, dh_make, cdbs, alien и т.д. Мы будем использовать только стандартные Linux утилиты, которые есть в любом современном дистрибутиве такие как sh, tar, cat, fakeroot, date. Нижеизложенную методику можно легко применить для создания deb пакета из любого программного продукта распространяемого производителем в бинарном виде.

Для примера давайте возьмём стандартную бинарную сборку Firefox с сайта Mozilla и создадим для него deb пакет. Для удобства все необходимые команды запишем в скрипт на стандартном shell, который можно легко будет изменить позднее под конкретные нужды.

Для начала зададим несколько переменных которые мы будем использовать в скрипте. В переменных зададим название пакета, версию, имя загружаемого архива, минорную версию, архитектуру собираемого пакета, имя создателя пакета и его почтовый адрес, ссылку для скачивания с сайта Mozilla и дату создания пакета в формате RFC-2822

#!/bin/sh

echo "Setting variables..."

PACKAGE=firefox
VERSION=3.5.5
FILE=${PACKAGE}-${VERSION}.tar.bz2
MINOR=1
ARCH=i386
USER='Your Name'
EMAIL='your@e-mail.here'
URL=ftp://ftp.mozilla.org/pub/firefox/releases/${VERSION}/linux-i686/ru/${PACKAGE}-${VERSION}.tar.bz2
DATE=`date -R`
...


Скачаем бинарный пакет Firefox с сайта производителя, если нет его в локальной директории:

...
echo "Downloading package..."
[ ! -f ${FILE} ] && wget $URL
...


Стандартный бинарный пакет Debian состоит из двух архивов data.tar.gz и control.tar.gz и файла debian-binary, которые запакованы в архив командой ar. С помощью ar собираются статические библиотеки из объектных файлов. Ее можно найти в любом дистрибутиве в пакете binutils.

debian-binary — содержит версию бинарной сборки. На момент написания статьи текущая версия была 2.0.
data.tar.gz — содержит данные которые будут установлены в систему пользователя
control.tar.gz — содержит файл control с метаданными о пакете и вспомогательные скрипты для установки удалении пакета.

Такое разделение позволяет быстро извлекать информацию о пакете прямо из пакета без разархивирования всего пакета.

Давайте создадим файлы которые необходимо будет добавить в control.tar.gz

...
echo "Creating control file..."

cat > control << EOF;
Package: ${PACKAGE}
Version: ${VERSION}-${MINOR}
Maintainer: ${USER} <${EMAIL}>
Section: non-free/web
Architecture: ${ARCH}
Homepage: http://www.firefox.com/
Priority: optional
Replaces: mozilla-firefox
Provides: www-browser
Depends: fontconfig, psmisc, procps, libc6, libgcc1, libstdc++6
Description: This is the original version of Firefox from Mozilla
Package built with dh-firefox utility
EOF

cat > debian-binary << EOF;
2.0
EOF
...


Создадим структуру каталогов будущего пакета и добавим README, changelog.Debian и файл с лицензией, который возьмем из репозитария с исходниками Firefox. Чтобы у пользователя они занимали меньше места на диске их мы сразу запакуем:

...
echo "Creating doc files..."
rm -Rf usr
mkdir -p usr/share/doc/firefox

cat << EOF | gzip -9 - > usr/share/doc/${PACKAGE}/changelog.Debian.gz
firefox (${VERSION}-1) unstable; urgency=low

* Initial release.

-- ${USER} <${EMAIL}> ${DATE}

EOF

cat << EOF | gzip -9 - > usr/share/doc/${PACKAGE}/README.Debian.gz
Firefox has been packged by dh-firefox utility.
EOF

echo "Downloading copyright file..."
#wget -O - http://hg.mozilla.org/mozilla-central/raw-file/f09961c1d5fa/LICENSE \
#> usr/share/doc/${PACKAGE}/copyright
[ ! -f LICENSE ] && wget http://hg.mozilla.org/mozilla-central/raw-file/f09961c1d5fa/LICENSE.
cp LICENSE usr/share/doc/${PACKAGE}/copyright
...


Создадим desktop файл для рабочих окружении которые поддерживают стандарты freedesktop.

...
echo "Creating desktop file..."
mkdir -p usr/share/applications
cat << EOF > usr/share/applications/firefox.desktop
[Desktop Entry]
Encoding=UTF-8
Name=Firefox Web Browser
Name[ru]=Веб браузер Firefox
Comment=Browse the World Wide Web
Comment[ru]=Просмотрщик для WWW
GenericName=Web Browser
GenericName=Веб браузер
Exec=firefox-original %u
Terminal=false
X-MultipleArgs=false
Type=Application
Icon=/usr/share/firefox/icons/mozicon128.png
Categories=Network;WebBrowser;
MimeType=text/html;text/xml;application/xhtml+xml;application/xml;application/vnd.mozilla.xul+xml;application/rss+xml;applicat
StartupWMClass=Firefox-bin
StartupNotify=true
EOF
...


Файловое имя /usr/bin/firefox используется в пакете iceweasel. Конечно можно использовать утилиту dpkg-divert для подстановки и замены файлов в системе, но мы будем придерживаться условий задачи и не будем использовать стандартные утилиты Debian. Поэтому мы просто создадим ссылку с другим именем на скрипт запуска firefox. Эту ссылку назовем firefox-original.



...
mkdir -p usr/bin
ln -s ../share/firefox/firefox usr/bin/firefox-original
...


Теперь создадим три скрипта postinst, prerm и postrm, которые будут запускаться после установки и перед удалением пакета и после удаления. Из этих скриптов запускаются утилиты, которые обновят базу desktop файлов, базу MIME типов и алтернатив.

...
echo "Creating install/uninstall scripts..."

cat << EOF > postinst
#!/bin/sh -e

if [ "\$1" = "configure" ] || [ "\$1" = "abort-upgrade" ] ; then
update-alternatives --install /usr/bin/x-www-browser \\
x-www-browser /usr/bin/firefox-original 70
update-alternatives --install /usr/bin/mozilla mozilla \\
/usr/bin/firefox-original 0
fi

if [ -x /usr/sbin/update-desktop-database ]; then
/usr/sbin/update-desktop-database
fi

if [ "\$1" = "configure" ] && [ -x "\`which update-mime 2>/dev/null\`" ]; then
update-mime
fi

if [ "\$1" = "configure" ] && which update-desktop-database >/dev/null 2>&1 ; then
update-desktop-database -q
fi

EOF

cat << EOF > postrm
#!/bin/sh -e

#if [ -x "\`which update-menus 2>/dev/null\`" ]; then update-menus ; fi
if which update-mime >/dev/null 2>&1; then update-mime; fi
if [ "\$1" = "remove" ] && which update-desktop-database >/dev/null 2>&1 ; then
update-desktop-database -q
fi

EOF

cat << EOF > prerm
#!/bin/sh -e

if [ "\$1" = "remove" ] || [ "\$1" = "deconfigure" ] ; then
update-alternatives --remove x-www-browser /usr/bin/firefox-original
update-alternatives --remove mozilla /usr/bin/firefox-original
fi

EOF

chmod +x postinst postrm prerm
...


Распакуем полученный бинарный архив с сайта Mozilla в директорию user/share. Также уберем флаг исполнения с динамически подгружаемых библиотек и подсчитаем контрольные суммы всех файлов:

...
echo "Unpacking ${PACKAGE} ${VERSION} archive..."

tar -C usr/share -xjf firefox-${VERSION}.tar.bz2

echo "Fixing permission for *.so files..."
find usr/share -type f -name *.so -exec chmod -x {} \;

echo "Calculating MD5 sums..."
find usr -type f -exec md5sum {} >> md5sums \;
...


Теперь пришло время упаковать все созданные файлы в архивы и сложить их в deb файл:

...
echo "Creating data.tar.gz ..."
fakeroot tar cvfz data.tar.gz usr

echo "Creating control.tar.gz ..."
fakeroot tar cvfz control.tar.gz control postinst postrm prerm

echo "Creating ${PACKAGE}_${VERSION}-${MINOR}_${ARCH}.deb ..."
rm -f ${PACKAGE}-${VERSION}-${MINOR}.deb
ar r ${PACKAGE}_${VERSION}-${MINOR}_${ARCH}.deb debian-binary control.tar.gz data.tar.gz
echo "The END..."


Чтобы убедится в том что deb файл создан корректно, запустим проверку на валидность:

man@work:~/tmp/firefox$ lintian -v firefox_3.5.5-1_i386.deb
N: Setting up lab in /tmp/UmQRWztbi7 ...
N: Processing 1 packages...
N: ----
N: Processing binary package firefox (version 3.5.5-1) ...
W: firefox: binary-without-manpage usr/bin/firefox-original
W: firefox: new-package-should-close-itp-bug
N: Removing /tmp/UmQRWztbi7 ...
man@work:~/tmp/firefox$


Как мы видим, пакет имеет только два несущественных предупреждения. Теперь можно его установить и насладится проделанной работой :)

dpkg -i firefox_3.5.5-1_i386.deb


Спасибо, за внимание!

P.S. Мне нужно то что было у всех зарегистрированных здесь пользователей ;)

Перенос FreeBSD7.1 из реального окружения в виртуальное. HYPER-V.

После выхода Windows Server 2008 покоя не давал HYPER-V. Уж очень хотелось установить его и завести виртуальных машин, да побольше. Но ни времени, ни ресурсов, под это дело, не хватало. Однажды, на работе на складе мы нашли сервер … пауза … нет правда! Настоящий IBM x3400. Как он туда попал до сих пор «наверху» понять не могут, по-моему. Примечание, фирма продает компьютеры. Сервер какое-то время стоял без дела, после чего было решено сделать его основным для разработчиков сайта. На него поставили FreeBSD, настроили все, апач, пхп, битрикс и т.п. Так прошло лето…

Однажды я про него вспомнил и решил немного подвинуть программистов и самому попользоваться сервером в целях самообразования, а также чтобы перенести туда часть виртуальных машин с ноутбука.

Часть1. Настойка и установка Windows Hyper-V Server 2008R2



Начнем с установки и настройки HYPER-V, как части Windows Hyper-V Server 2008 R2. Рекомендую выбрать английскую версию, т.к. большинство руководств по настройке содержит в себе английские названия служб, утилит и т.п. и сопоставить их с русскими, порой занимает лишнее время. Также в одном из топиков на social.technet.microsoft.com я встречал фразу «пункт 1 пропускаем, т.к. он все равно в русской версии не будет работать» и действительно когда настраиваешь в русской версии пункт 4-1 в «scconfig.cmd», то всегда будет появляться окошко «Не удалось перенастроить брандмауэр Windows». Данная операционная система построена на базе Windows Server Core и не предполагает графических утилит для настройки, однако ей очень удобно можно управлять через консоли MMC с другого компьютера, например на Windows 7. После установки, система выглядит так:

image

Как видно, тут присутствует пустое окно cmd.exe, а также запущенный командный файл scconfig.cmd, которые запускаются автоматически при входе в систему. Первое, что необходимо сделать, это ввести сервер в домен и присвоить ему имя. Можно конечно не вводить машинку в домен, но тогда будут проблемы с правами, которые, если честно, мне так и не удалось победить до конца. При наличии вас в группе, которая попадет в локальные администраторы, при применении доменных политик (по умолчанию, это «Администраторы домена»), вы получите все административные функции, кроме тех, что блокируются брандмауэром на вашем сервере. Компьютер, с которого вы будете управлять, тоже должен быть в составе домена.
Настройка доступа для сервера в рабочей группе описана в форуме Technet

Далее о брандмауэре. Для разрешения доступа по MMC и через «Удаленный рабочий стол» необходимо перейти в окно «scconfig.cmd» и выбрать пункт номер 7, а также номер 4, где включить mmc, а также разрешить использование Powershell. «Удаленное управление через диспетчер сервера» мне включить не удалось из-за проблем с брандмауэром на моей Windows 7.
Для управления сервером из Windows 7 вам потребуется набор административных консолей mmc – RSAT. Скачать его можно с сайта Майкрософт

На данном этапе возникают 2 проблемы, это не работающие «Диспетчер устройств» и «Управление дисками» через консоль «Управление компьютером».

  • Создаем новую консоль mmc и сохраняем ее куда-нибудь.
  • Открываем ее под пользователем домена, с административными правами. (если на исполняемом файле или ярлыке нажать «Shift+ПКМ» то в контекстном меню появится несколько дополнительных функций.
  • Добавляем туда «Редактор объектов групповой политики» и вместо «локальный компьютер», выбираем ваш Hyper-V Server.
  • Переходим в «Конфигурация компьютера\Административные шаблоны\Система\Установка устройств» и включаем политику «Разрешить удаленный доступ к устройствам Plug’n’Play” для того, чтобы удаленно заработал «Диспетчер устройств»

Стоит отметить, что диспетчер устройств будет в режиме read only.

Для удаленного управления дисками нужно в консоли сервера выполнить:

netsh advfirewall firewall set rule group=“Remote Volume Management” new enable=yes

Для удаленного запуска оснастки по управлению брандмауэром:

netsh advfirewall firewall set rule group=“Windows Firewall Remote Management” new enable=yes

Все. Теперь у вас есть доступ к 2м основным компонентам на сервере
— Управление компьютером (Computer management);
— Диспетчер Hyper-V (Hyper-V manager)

Часть 2. Миграция FreeBSD 7.1 на Hyper-V Server.



Во-первых, необходимо выполнить резервное копирование FreeBSD на другой диск, чтобы эксперимент с переносом не закончился плачевно. Т.к. просто клонировать образ диска на другой, с помощью стандартных утилит (Acronis, Partition Magic), не получится, то обойдемся средствами FreeBSD.
Берем новый диск, подключаем его рабочей freebsd и создаем на нем разделы и слайсы нужных нам размеров, а также делаем диск загрузочным и устанавливаем загрузчик FreeBSD. В общем случае, процедура резервного копирования заключается в следующих командах:

# создаем файловую систему UFS на новом слайсе.
# newfs /dev/ad6s1a
# монтируем новый слайс в нужную нам директорию
# mount /dev/ad6s1a /mnt
# cd /mnt
# делаем резервную копию раздела / в примонтированный слайс
# dump 0af - / | restore xf -


Дальше таким же образом для каждого вашего раздела, зависит от того какие вы создавали на этапе установки FreeBSD, например /usr, /tmp и т.п.
После бекапа всех разделов, нужно открыть /etc/fstab на новом диске и поправить таблицу монтирования разделов под ваш новый жесткий диск, после этого, перемещать диск, в другие разъемы SATA или т.п., не рекомендуется.
Все, теперь пытаемся загрузиться с нового диска, и если загрузка успешна, откладываем его в сторону.
Приступим к миграции в Hyper-V. К серверу с установленной Hyper-V, необходимо подключить диск с FreeBSD.
Запускаем Windows 2008 и подключаемся к ней через консоль Hyper-V manager (диспетчер Hyper-V), запущенной с правами доменной учетной записи. Нажимаем ПКМ на нужном сервере в консоли и выбираем «создать\жесткий диск» в открывшемся окне выбираем тип диска, размер, и главное в пункте «Настройка диска» выбираем «Копировать содержимое указанного жесткого диска» где отмечаем наш HDD с FreeBSD. После окончания процесса создаем новую виртуальную машину и подключаем к ней наш новый виртуальный диск.
Запускаем новую виртуальную машину! И видим что она не запускается, вернее, на этапе загрузки доходит до момента монтирования файловых систем и не может это сделать т.к. в файле «/etc/fstab» указан другой диск.
Поправить это просто, в окне с FreeBSD мы увидим приглашение смонтировать ФС вручную

mount:

что мы и сделаем, выполнив команду

mount /dev/ad0s1a /

вам нужно будет указать свой слайс, в котором находится корневой раздел. Загрузка продолжится, после этого логинимся в систему под root и правим /etc/fstab под наши названия дисков.
Перезагрузка и проверка, что ОС загрузилась успешно…

FreeBSD действительно загрузилась успешно, но как и пишет Андрей Бешков в своем блоге присутствует проблема с DHCP, которая решается либо отключение DHCP и установкой ручных параметров, либо добавлением дополнительных параметров для сетевой карты

ifconfig_de0=”DHCP media 100baseTX mediaopt full-duplex”

Вот и все! Удачной всем виртуализации =)

Blackberry Ant Tools — система сборки проектов для Blackberry.

Стандартный диалог с заказчиком при обсуждении очередного проекта для Blackberry выглядит примерно так:

PM (с надеждой): А вам версии приложения для каких платформ нужны?..
Заказчик : Для всех!!!

Вот тут-то, %username%, начинается тот самый кошмар, имя которому «разработка для Blackberry».

Особенности процесса компиляции.

Во-первых, у Blackberry достаточно много версий их ОС. Не буду перечислять их все, отмечу лишь, что как правило требуется поддержка как минимум диапазона 4.1-4.7, реже версий меньше 4.0. Соответственно, приходится как минимум создавать билд для каждой из платформ, так как одни и те же функции API работают на них по-разному.

Во-вторых, вне зависимости от установленной версии ОС, у телефонов Blackberry большой набор возможных разрешений. Значит, скорее всего придется делать отдельный билд приложения для каждого из этих разрешений.

В-третьих, для того, чтобы скомпилировать приложение для Blackberry надо:

  1. Нажать кнопку F7 в Blackberry IDE (не сложно ведь, правда?)
  2. Подписать получившийся .cod-файл, что включает в себя
    • Дважды щелкнуть по .cod-файлу мышкой
    • Ввести пароль для подписи
    • Нажать OK
  3. Разархивировать получившийся .cod-файл. Здесь имеет смысл пояснить — Blackberry JDE преобразует стандартный формат бинарника .JAR в формат Blackberry OS .cod. Более того, JDE автоматически разбивает слишком большой .cod файл на несколько более мелких кусочков и затем зипует их в архив с тем же расширением. Но установить такой архив на телефоне невозможно — надо сначала раззиповать его на кучу .cod-файлов
  4. И наконец, надо скопировать все .cod-файлы и .jad-файл на какой-нибудь сервер — ибо проще всего установить приложение на Blackberry можно лишь скачав его из браузера, установленного на телефоне


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

Как же все сделать по-человечески?

Сначала, перелопатив документацию о RIMовском компиляторе, я сделал несколько батников, с помощью которых компилировал приложения. Но потом и их стало нехватать. В один прекрасный день я наткнулся на проект Blackberry Ant Tools — и я забыл о получасовой компиляции навсегда.

Основные возможности расширения:
  • С помощью тега <jdp> настроить все параметры проекта — название приложения, его иконку, импорт нужных библиотек и многое другое.Выглядеть это будет примерно вот так:

    <jdp title="Kick Ass App" vendor="Slashdev" version="1.0" type="cldc" />
  • Директива <sigtool> позволяет избежать ручной подписи файлов
  • Директива <define> дает возможность указать директивы препроцессора для компилятора RIM (да, у них даже есть препроцессинг!). В JDE версии < 4.7 это можно сделать только редактируя вручную файл проекта JDE (.jdp)

Есть еще множество плюшек, с которыми можно ознакомиться здесь

Примечание.

Единственной серьезной проблемой данного расширения было то, что при компиляции проекта иногда ресурсы (картинки, звуки итп) складывались в корень .cod-файла. Оказалось, что «иногда» — это когда для директивы <rapc> указан аттрибут destdir, отличающийся от корневой директории проекта. Таким образом, проблема оказалась в компиляторе от RIM. Решение очевидно — копировать бинарники с помощью стандартных антовских директив.

Вот, собственно и все %).