воскресенье, 19 августа 2012 г.

Первые функциональные шаги


Haskell — пока еще единственный язык программирования, в котором есть оператор «фтопку» 
© http://absurdopedia.wikia.com/
Увы, я еще не могу сказать, что постиг haskell) Но процесс изучения движется, хоть и времени на него остается все меньше и меньше.
В качестве первой попытки написать что-нибудь рабочее и полезное я выбрал сборщик гуглохаков с www.exploit-db.com, код которого и опишу в данной заметке.
Haskell - очень любопытный язык программирования. Бывает, что в процессе его изучения ты думаешь, что вот-вот постигнешь его - но через день понимаешь, что еще далек до просветления)
Но не стоит разводить лирику - многие и так считают меня фанатиком. Поэтому пару слов как и зачем.
Несмотря на то, что сборщик - штука полезная, все, что я опишу здесь - в основном предназначено для меня самого. Привести мысли в порядок, понять, что я уже четко осознаю, а что - еще нет. Поэтому код будет далек от совершенства, хотя изначально я хотел добиться именно его) Однако, в виду отсутствия достаточного количества свободного времени, я решил пока остановиться на том, что хотя бы работает.

Да-да, не стесняемся, пишем в vim'е) К тому же есть замечательные плагины neco-ghc и haskell.vim (первый добавляет omnicomplete для haskell, второй - разные приятные мелочи).

Ну а теперь немного о том, что из себя представляет код сборщика.
Во-первых, гуглохаки расположены по адресу www.exploit-db.com/ghdb/{number}, где number - это порядковый номер хака. Соответственно, в программе нам необходимо будет загружать эту страницу и выбирать в ней значение атрибута href у первой ссылки, которая последует за первым на странице заголовком второго уровня h2.

Во-вторых, для разбора html будем использовать известный и, на мой взгляд, невероятно удобный пакет HXT. Соответственно, понадобится его установить:

cabal install hxt

Теперь непосредственно к коду. Для начала подключим все необходимые библиотеки:

import System.Environment
import Text.HandsomeSoup
import Text.XML.HXT.Core
import Control.Monad
import System.IO

System.Evironment & System.IO - для получения входных параметров и вывода собранных хаков в файл. Text.HandsomeSoup - для загрузки разметки страницы. Text.XML.HXT.Core - для разбора страницы и поиска на ней тегов.

У нас будет всего 3 функции, одна из которых будет точкой входа:
main = do
        [number,filePath] <- getArgs
        outh <- openFile filePath WriteMode
        result <- collect outh (read number :: Int)
        hClose outh

Для начала в number и filePath получаем необходимые параметры - сколько хаков собрать и в какой файл их записать. Затем с помощью функции openFile открываем выбранный файл для записи, а его хендл помещаем в outh для дальнейшего использования. Затем вызываем функцию сбора хаков collect, передавая ей на вход файл и количество хаков, приведенное к целому числу. После всех операций закрываем файл.

Основная функция сборки выглядит так:
collectHelper file 0 exploits = return exploits
collectHelper file number exploits = do
        doc <- fromUrl . exploitDbUrl $ number
        exploit <- runX $ doc >>> css "h2 a" >>> getAttrValue "href"
        hPutStrLn file (head exploit)
        collectHelper file (number - 1) (exploit:exploits)

collect file number = collectHelper file number []

Она разбита на две части для удобного использования рекурсии. Собственно, collect просто передает параметры collectHelper'у, а заодно и список, в который та будет добавлять новые найденные хаки в виде строк.

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

Для начала, загрузим разметку по адресу, который сформируем из исходного плюс номер страницы, с помощью функции fromUrl. Эта функция конкатенирована с exploitDbUrl, которая возвращает строку исходного адреса с помощью оператора "·" (точка) :

exploitDbUrl number = "http://www.exploit-db.com/ghdb/" ++ show number ++ "/"


С помощью оператора "$" передаем в итоговую конкатенированную функцию параметр number - номер страницы. Результат помещаем в doc.
Затем с помощью HXT начинаем поиск нужных тегов. Для начала вызываем runX(напрямую функции из HXT не вызвать) и передаем в параметрах описание того, что хотим сделать. В данном случае указываем, что в документе doc необходимо найти тег с типом h2, а в нем тег ссылки a, и извлечь из этой ссылки значение атрибута href.

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

На этом код завершен) Расположен он здесь. Дальше дело техники - скомпилировать и запустить:

ghc -o collector collector.hs
./collector 10 exploits


0коммент.:

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