<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Сайт о программировании</title>
	<atom:link href="http://codesources.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://codesources.net</link>
	<description>Простые советы для непростых задач</description>
	<lastBuildDate>Wed, 01 Feb 2012 14:41:18 +0000</lastBuildDate>
	<language>ru</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Начало работы с CakePHP по-русски</title>
		<link>http://codesources.net/nachalo-raboty-s-cakephp-po-russki/</link>
		<comments>http://codesources.net/nachalo-raboty-s-cakephp-po-russki/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:41:18 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Фреймворки]]></category>

		<guid isPermaLink="false">http://codesources.net/?p=81</guid>
		<description><![CDATA[Скоро товарищей, пишущих по старинке веб-страницы на голом php, можно будет показывать в зоопарках  Наступает эра Фреймворков. И это хорошо,<br /><a class="more-link" href="http://codesources.net/nachalo-raboty-s-cakephp-po-russki/">keep reading</a>]]></description>
			<content:encoded><![CDATA[<p>Скоро товарищей, пишущих по старинке веб-страницы на голом php, можно будет показывать в зоопарках <img src="http://web.archive.org/web/20081227155732im_/http://codesources.net/wp-includes/images/smilies/icon_smile.gif" alt=":-)" /><br />
Наступает эра Фреймворков. И это хорошо, потому что программист избавлен от множества рутинных задач,<br />
и может сосредоточиться на создании логики приложения.<br />
В CakePHP используется подход MCV (Model &#8212; View &#8212; Controller). Теперь вместо одного файла *.php на одну<br />
страничку их будет целых три минимум %) &#8212; один для модели (описывает связь формы с базой данных, имеет<br />
расширение php), thtml файл будет описывать внешний вид страницы (view), третий (контроллер страницы с<br />
расширением php будет реализовывать логику формы и будет содержать методы, дополняющие функциональность<br />
родительского класса AppController.<br />
Что ж, начнем печь приложения как пирожки )) Но для этого придется преступить через себя и принять новый подход к программированию. Оно того стоит ^_^</p>
<p>&nbsp;</p>
<p>корневую директорию веб-сервера и наберите в браузере адрес сайта. Должна появиться приветственная страничка</p>
<blockquote><p><em>Your database configuration file is not present.</em></p></blockquote>
<p>Нужно прописать соединение с базой данных. Делается это в директории <strong>app/config</strong> - берем за основу <strong>database.php.default</strong> и переименовываем его в <strong>database.php</strong>. Прописываем в файле параметры соединения с базой. Если все ОК то CakePHP сообщит об этом.</p>
<blockquote><p><em>Your database configuration file is present.</em></p>
<p><em>Cake is able to connect to the database.</em></p></blockquote>
<p>Все готово для творчества.</p>
<h2>Модуль новостей</h2>
<p>Напишем простейший модуль новостей, чтобы проиллюстрировать технологию создания сайтов на Фреймворке. Создадим в базе данных таблицу posts, в которой будут храниться новости.</p>
<pre><em>CREATE TABLE `posts` ( `id` bigint(20) NOT NULL auto_increment, `post_date` date NOT NULL default '0000-00-00', `post_header` varchar(255) NOT NULL default '', `post_text` text NOT NULL, `archived` tinyint(4) NOT NULL default '0', PRIMARY KEY (`id`) )</em></pre>
<p>Вопрос: почему бы таблицу не назвать “news”? Таблица должна иметь имя сущности во множественном числе.<br />
В английском “новости” news всегда употребляется во множественном числе, тем более слово new является<br />
ключевым в php, что приводит к ошибке.</p>
<h2>Модель</h2>
<p>В папке <strong>app/models</strong> создадим файл модели <strong>post.php</strong></p>
<pre><em>&lt;?php class Post extends AppModel { var $name = 'Post'; var $validate = array( 'post_header' =&gt; VALID_NOT_EMPTY, 'post_text' =&gt; VALID_NOT_EMPTY, 'post_date' =&gt; VALID_NOT_EMPTY ); } ?&gt;</em></pre>
<p>В массиве $validate описываются ограничения накладываемые логикой приложения на данные. Мы запретили пустые поля<br />
Текст, Заголовок и Дата.</p>
<h2>Контроллер</h2>
<p>В папке <strong>app/controllers</strong> создадим файл <strong>posts_controller.php</strong>. Создадим объект, который реализует основные функции<br />
модуля новостей: отображение списка (index), просмотр новости (view), удаление новости (delete), добавления новости (add).</p>
<pre><em>&lt;?php class PostsController extends AppController // наследуемся // от AppController { var $name = 'Posts'; function index() // эта функция должна быть обязательно { $this-&gt;set('posts', $this-&gt;Post-&gt;findAll()); // выборка всех записей //из таблицы $this-&gt;pageTitle = 'Новости'; } function view($id) // просмотр новости { $this-&gt;Post-&gt;id = $id; $this-&gt;set('post', $this-&gt;Post-&gt;read()); } function add() // добавление новости { if (!empty($this-&gt;data)) { if ($this-&gt;Post-&gt;save($this-&gt;data)) { $this-&gt;flash('Новость была успешно добавлена','/posts'); // flash выдает сообщение пользователю в виде ссылки на страницу, указанной во втором аргументе функции } } } function delete($id) // удаление новости { $this-&gt;Post-&gt;del($id); $this-&gt;flash('Новость была удалена', '/posts'); } function edit($id = null) // редактирование новости { if (empty($this-&gt;data)) { $this-&gt;Post-&gt;id = $id; $this-&gt;data = $this-&gt;Post-&gt;read(); } else { if ($this-&gt;Post-&gt;save($this-&gt;data['Post'])) { $this-&gt;flash('Новость была отредактирована','/posts'); } } } } ?&gt;</em></pre>
<h2>Виды</h2>
<p>В папке app/views создадим папку posts. В этой папке необходимо создать файлы видов для каждой функции,<br />
кроме функции Delete, которая не требует своего вида, а использует только функцию flash.<br />
<strong>app/views/posts/index.thtml</strong></p>
<pre>&lt;h1&gt;Новости&lt;/h1&gt;

&lt;?

// длина строки новости, которая будет
// показываться до "далее" или "..."

  $news_teaser_length = 12;

  $news_more_annex = ' &gt;&gt;';

  if (count($posts)&gt;0)

  {

?&gt;

&lt;table&gt;

&lt;tr&gt;

 &lt;th&gt;Дата&lt;/th&gt;

 &lt;th&gt;Заголовок&lt;/th&gt;

 &lt;th&gt;Текст&lt;/th&gt;

 &lt;th&gt; &lt;/th&gt;

 &lt;th&gt; &lt;/th&gt;

&lt;/tr&gt;

    &lt;?php foreach ($posts as $post): ?&gt;

    &lt;tr&gt;

        &lt;td&gt;

        &lt;?php

          echo $post['Post']['post_date'];

        ?&gt;

        &lt;/td&gt;

        &lt;td&gt;

        &lt;?php

        echo $post['Post']['post_header'];

        ?&gt;

        &lt;/td&gt;

        &lt;td&gt;

            &lt;?php

            // если новость слишком длиная
            //(слов больше чем $news_teaser_length)
            // то надо показать только $news_teaser_length
            // слов

            $line = $post['Post']['post_text'];

            $tmpline = preg_replace("/s+/", " ", $line);

            $mes_words = explode(' ',$tmpline);

            $text = "";

            if (count($mes_words) &gt;= $news_teaser_length)

            {

              $last_word = $mes_words[$news_teaser_length -1];

              for ($i = 0; $i &lt;= $news_teaser_length -1 ; $i++)

              {

                $text = $text.$mes_words[$i]." ";

              }

              echo $html-&gt;link($text.$news_more_annex,

                       '/posts/view/'.$post['Post']['id']);

            }

            else

            {

              echo $html-&gt;link($post['Post']['post_text'],
                  '/posts/view/'.$post['Post']['id']);

            }

            ?&gt;

        &lt;/td&gt;

        &lt;td&gt;

            &lt;?php

            echo $html-&gt;link('Редактировать',
               '/posts/edit/'.$post['Post']['id']);

           ?&gt;

        &lt;/td&gt;

        &lt;td&gt;

           &lt;?php

            echo $html-&gt;link('Удалить',
               '/posts/delete/'.$post['Post']['id']);

           ?&gt;

        &lt;/td&gt;

    &lt;/tr&gt;

    &lt;?php endforeach; ?&gt;

&lt;/table&gt;

&lt;?

}

  else

  {

    echo "&lt;p&gt;Новостей пока нет&lt;/p&gt;";

  }

?&gt;

 &lt;p&gt;&lt;a href="/posts/add"&gt;Добавить новость?&lt;/a&gt;&lt;/p&gt;</pre>
<p><strong>app/views/posts/add.thtml</strong></p>
<pre>&lt;h1&gt;Добавить новость&lt;/h1&gt;

&lt;form method="post" action="&lt;?php
  echo $html-&gt;url('/posts/add')?&gt;"&gt;

    &lt;p&gt;

        Название: &lt;br&gt;

        &lt;?php echo $html-&gt;input('Post/post_header',
           array('size' =&gt; '40'))?&gt;

        &lt;?php echo $html-&gt;tagErrorMsg('Post/post_header',
           'Название не может быть пустым.') ?&gt;

    &lt;/p&gt;

    &lt;p&gt;

        Дата: &lt;br&gt;

        &lt;?php echo $html-&gt;input('Post/post_date',
           array('size' =&gt; '40'))?&gt;

        &lt;?php echo $html-&gt;tagErrorMsg('Post/post_data',
            'Дата не может быть пустой.') ?&gt;

    &lt;/p&gt;

    &lt;p&gt;

        Текст:&lt;br&gt;

        &lt;?php echo $html-&gt;textarea('Post/post_text',
           array('rows'=&gt;'10')) ?&gt;

        &lt;?php echo $html-&gt;tagErrorMsg('Post/post_text',
             'Сообщение не может быть пустым.') ?&gt;

    &lt;/p&gt;

    &lt;p&gt;

        &lt;?php echo $html-&gt;submit('Отправить') ?&gt;

    &lt;/p&gt;

&lt;/form&gt;</pre>
<p><strong>app/views/posts/edit.thtml</strong></p>
<pre>&lt;h1&gt;Редактирование новости&lt;/h1&gt;

&lt;form method="post" action="&lt;?php
   echo $html-&gt;url('/posts/edit')?&gt;"&gt;

    &lt;?php echo $html-&gt;hidden('Post/id'); ?&gt;

    &lt;p&gt;

        Заголовок:&lt;br&gt;

        &lt;?php echo $html-&gt;input('Post/post_header',
           array('size' =&gt; '40'))?&gt;

        &lt;?php echo $html-&gt;tagErrorMsg('Post/post_header',
           'Необходим заголовок.') ?&gt;

    &lt;/p&gt;

    &lt;p&gt;

        Дата:&lt;br&gt;

        &lt;?php echo $html-&gt;input('Post/post_date',
           array('size' =&gt; '40'))?&gt;

        &lt;?php echo $html-&gt;tagErrorMsg('Post/post_date',
           'Необходима дата.') ?&gt;

    &lt;/p&gt;

    &lt;p&gt;

        Текст:&lt;br&gt;

        &lt;?php echo $html-&gt;textarea('Post/post_text',
           array('rows'=&gt;'10')) ?&gt;

        &lt;?php echo $html-&gt;tagErrorMsg('Post/post_text',
            'Текст не должен быть пустым!') ?&gt;

    &lt;/p&gt;

    &lt;p&gt;

        &lt;?php echo $html-&gt;submit('Сохранить') ?&gt;

    &lt;/p&gt;

&lt;/form&gt;</pre>
<p><strong>app\views\posts\view.thtml</strong></p>
<pre>&lt;h1&gt;&lt;?php echo $post['Post']['post_header']?&gt;&lt;/h1&gt;

&lt;p&gt;&lt;small&gt;Создана: &lt;?php
  echo $post['Post']['post_date']?&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;&lt;?php echo $post['Post']['post_text']?&gt;&lt;/p&gt;</pre>
<p>Фу: вроде все почти готово <img src="http://web.archive.org/web/20081227155732im_/http://art.7e-nebo.ru/wp-includes/images/smilies/icon_smile.gif" alt=":-)" /> Теперь осталось перенаправить пользователя c приветственной странички CakePHP на страничку <strong>app/posts</strong>.</p>
<p>Для этого идем в <strong>app/config/routes.php</strong> и меняем там строку</p>
<blockquote><p>$Route-&gt;connect(’/&#8217;, array(’controller’ =&gt; ‘pages’, ‘action’ =&gt; ‘display’, ‘home’));</p></blockquote>
<p>на строку</p>
<blockquote><p>$Route-&gt;connect (’/&#8217;, array(’controller’=&gt;’posts’, ‘action’=&gt;’index’));</p></blockquote>
<p>Теперь можете в браузере набрать адрес сайта и понаблюдать CakePHP в действии <img src="http://web.archive.org/web/20081227155732im_/http://art.7e-nebo.ru/wp-includes/images/smilies/icon_smile.gif" alt=":)" /></p>
<h2>Небольшая обработка напильником</h2>
<p>Полюбовавшись на полученное приложение, пытливый читатель скоро почувствует разочарование. Как поменять стиль страницы,<br />
убрать маленький баннер снизу и гордое “CakePHP Rapid Development” в верху каждой страницы? Как заставить функцию<br />
flash отображать текст по-русски? Для этой цели служат layouts. (Долго думал, как перевести layouts и решил<br />
остановиться на слове “шаблоны”). Бодро идем в папку <strong>app/views/layouts</strong> и помещаем там нужные файлы:<br />
<strong>default.thtml</strong>, например, такой:</p>
<pre>&lt;html&gt;

&lt;head&gt;

&lt;META http-equiv="content-type"
content="text/html;charset=windows-1251" /&gt;

&lt;title&gt;CAKE_TEST&lt;?php echo $title_for_layout?&gt;&lt;/title&gt;

&lt;link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"&gt;

&lt;link rel="stylesheet" type="text/css" href="/css/cake.generic.css" /&gt;

&lt;/head&gt;

&lt;body&gt;

            &lt;div id="wrapper"&gt;

                        &lt;div id="container"&gt;

            &lt;div id="content"&gt;

                                               &lt;?php echo $content_for_layout ?&gt;

                                   &lt;/div&gt;

                        &lt;/div&gt;

            &lt;/div&gt;

&lt;/body&gt;

&lt;/html&gt;</pre>
<p>Вылечим функцию flash от любви к utf8. Для этого разместим там же <strong>flash.thtml</strong></p>
<pre>&lt;html&gt;

&lt;head&gt;

&lt;title&gt;&lt;?php echo $page_title?&gt;&lt;/title&gt;

&lt;META http-equiv="content-type"
content="text/html;charset=windows-1251" /&gt;

&lt;?php if(Configure::read() == 0) { ?&gt;

&lt;meta http-equiv="Refresh" content="&lt;?php
  echo $pause?&gt;;url=&lt;?php echo $url?&gt;"/&gt;

&lt;?php } ?&gt;

&lt;style&gt;&lt;!--

P { text-align:center; font:bold small sans-serif }

A { color:red}

A:HOVER { text-decoration: underline; color:#44E }

--&gt;&lt;/style&gt;

&lt;/head&gt;

&lt;body&gt;

&lt;p&gt;&lt;a href="&lt;?php echo $url?&gt;"&gt;&lt;?php
  echo $message?&gt;&lt;/a&gt;&lt;/p&gt;

&lt;/body&gt;

&lt;/html&gt;</pre>
<p>Прототипы этих файлов можно увидеть в папке <strong>cake/libs/view/templates/layouts</strong></p>
<p>Вроде бы все <img src="http://web.archive.org/web/20081227155732im_/http://art.7e-nebo.ru/wp-includes/images/smilies/icon_smile.gif" alt=":)" /> Прошу php пуристов не критиковать мой код, ибо сделан он больше развлечения ради. Рекомендую сходить на cakeforge.org и скачать там мануал и помощь по API в формате chm. Удачи в печении плюшек! <img src="http://web.archive.org/web/20081227155732im_/http://art.7e-nebo.ru/wp-includes/images/smilies/icon_smile.gif" alt=":)" /></p>
]]></content:encoded>
			<wfw:commentRss>http://codesources.net/nachalo-raboty-s-cakephp-po-russki/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Справочник команд UNIX</title>
		<link>http://codesources.net/spravochnik-komand-unix/</link>
		<comments>http://codesources.net/spravochnik-komand-unix/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:37:06 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Unix]]></category>

		<guid isPermaLink="false">http://codesources.net/?p=79</guid>
		<description><![CDATA[Список наиболее часто используемых команд используемых при администрировании UNIX-серверов. Перезапуск bind /etc/init.d/bind9 reload Справочник будет пополняться Получить список открытых портов<br /><a class="more-link" href="http://codesources.net/spravochnik-komand-unix/">keep reading</a>]]></description>
			<content:encoded><![CDATA[<p>Список наиболее часто используемых команд используемых при администрировании UNIX-серверов.</p>
<h2>Перезапуск bind</h2>
<p>/etc/init.d/bind9 reload</p>
<p>Справочник будет пополняться</p>
<h2>Получить список открытых портов</h2>
<p>netstat -tpl</p>
]]></content:encoded>
			<wfw:commentRss>http://codesources.net/spravochnik-komand-unix/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Как сделать скриншот в Debian</title>
		<link>http://codesources.net/kak-sdelat-skrinshot-v-debian/</link>
		<comments>http://codesources.net/kak-sdelat-skrinshot-v-debian/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:35:54 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Debian]]></category>
		<category><![CDATA[Unix]]></category>

		<guid isPermaLink="false">http://codesources.net/?p=75</guid>
		<description><![CDATA[Существует несколько способов сделать сриншот. Имеются следующие пакеты: gkrellshoot &#8212; Plugin for gkrellm to lock the screen and make screenshots<br /><a class="more-link" href="http://codesources.net/kak-sdelat-skrinshot-v-debian/">keep reading</a>]]></description>
			<content:encoded><![CDATA[<p>Существует несколько способов сделать сриншот. Имеются следующие пакеты:</p>
<ul>
<li>gkrellshoot &#8212; Plugin for gkrellm to lock the screen and make screenshots</li>
<li>ksnapshot &#8212; Screenshot application for KDE</li>
<li>xbase-clients &#8212; miscellaneous X clients</li>
</ul>
<p>Если установлен ImageMagick, то можно набрать в консоли</p>
<div>
<div>$ import -frame screenshot.png</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://codesources.net/kak-sdelat-skrinshot-v-debian/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PureFTPd + MySQL в Debian</title>
		<link>http://codesources.net/pureftpd-mysql-v-debian/</link>
		<comments>http://codesources.net/pureftpd-mysql-v-debian/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:34:59 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Unix]]></category>

		<guid isPermaLink="false">http://codesources.net/?p=73</guid>
		<description><![CDATA[Это документ описывает как установить PureFTPd сервер, который использует виртуальных пользователей из базы данных MySQL вместо реальных пользователей системы. Это<br /><a class="more-link" href="http://codesources.net/pureftpd-mysql-v-debian/">keep reading</a>]]></description>
			<content:encoded><![CDATA[<p>Это документ описывает как установить PureFTPd сервер, который использует виртуальных пользователей из базы данных MySQL вместо реальных пользователей системы. Это позволит иметь тысячи пользователей ftp на одной машине. Кроме того, я покажу как использовать квотирование и ограничить входящую/исходящую пропускную способность. Пароли будут записаны в виде зашифрованных MD5 строк в базе данных.</p>
<p>Для администрирования базы данных MySQL вы можете использовать web-интерфейс, такой как phpMyAdmin, установка которого тоже будет описана в этом документе. phpMyAdmin &#8212; это удобный графический интерфейс, благодаря которому не надо будет ничего делать из командной строки.</p>
<p>В статье используется Debian Etch (Debian 4.0). У вас уже должна быть установлена базовая система Debian Etch, как это описано в первых шести главах этого учебника.</p>
<p>Эта статья является практической и не содержит теоретических отступлений.</p>
<p>Статья не дает каких-либо гарантий! Я не утверждаю что это единчтвенный способ настройки. Есть много способов, добиться той же цели. И я не гарантирую, что приведенный способ будет работать у вас.</p>
<h2>Предварительные комментарии</h2>
<p>В этой статье я использую имя хоста server1.example.com и IP адресом 192.168.0.100. Эти настройки у вас могут быть другими, поэтому вам надо будет заменить их на соответствующие.</p>
<h2>Установка MySQL и phpMyAdmin</h2>
<p>Всё можно установить одной командой:</p>
<div>
<div>apt-get install mysql-server mysql-client libmysqlclient15-dev phpmyadmin apache2</div>
</div>
<p>Создайте пароль для MySQL пользователя root (замените yourrootsqlpassword на пароль, который хотите использовать):</p>
<div>
<div>mysqladmin -u root password yourrootsqlpassword</div>
</div>
<p>Затем с помощью следующей команды проверьте какой адрес слушает MySQL.</p>
<div>
<div>netstat -tap | grep mysql</div>
</div>
<p>Если вывод выглядит примерно так:</p>
<div>
<div>tcp        0      0 localhost.localdo:mysql *:*                    LISTEN    2713/mysqld</div>
</div>
<p>то это означает, что MySQL слушает только localhost.localdomain, и вы можете безопасно использовать пароль, установленный ранее. Но если вывод выглядит так:</p>
<div>
<div>tcp        0      0 *:mysql *:*                     LISTEN     2713/mysqld</div>
</div>
<p>то вам нужно установить пароль MySQL для вашего имени хоста, иначе любой сможет получить доступ к базе данных и изменить данные:</p>
<div>
<div>mysqladmin -h server1.example.com -u root password yourrootsqlpassword</div>
</div>
<h2>Установка PureFTPd с поддержкой MySQL</h2>
<p>Для Debian доступен сконфигурированный пакет pure-ftpd-mysql. Установить его можно так:</p>
<div>
<div>apt-get install pure-ftpd-mysql</div>
</div>
<p>Затем создаем ftp группу (ftpgroup) и пользователя (ftpuser), на которых будут ссылаться все наши виртуальные пользователи. Замените group- и userid 2001 на номер, который свободен в вашей системе:</p>
<div>
<div>groupadd -g 2001 ftpgroup</p>
<p>useradd -u 2001 -s /bin/false -d /bin/null -c &#187;pureftpd user&#187; -g ftpgroup ftpuser</p></div>
</div>
<h2>Создание базы данных MySQL для PureFTPd</h2>
<p>Сейчас мы создадим базу данных, которую назовем pureftpd и MySQL пользователя pureftpd, которого демон PureFTPd будет использовать для подключения к базе данных pureftpd.</p>
<div>
<div>mysql -u root -p</p>
<p>CREATE DATABASE pureftpd;</p>
<p>GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ON pureftpd.* TO &#8217;pureftpd&#8217;@'localhost&#8217; IDENTIFIED BY &#8217;ftpdpass&#8217;;</p>
<p>GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP ON pureftpd.* TO &#8217;pureftpd&#8217;@'localhost.localdomain&#8217; IDENTIFIED BY &#8217;ftpdpass&#8217;;</p>
<p>FLUSH PRIVILEGES;</p></div>
</div>
<p>Замените строку ftpdpass на пароль, который вы хотите использовать для пользователя pureftpd. Затем создадим таблицу, которая будет нам нужна:</p>
<div>
<div>USE pureftpd;</p>
<p>CREATE TABLE ftpd (</p>
<p>User varchar(16) NOT NULL default &#187;,</p>
<p>status enum(&#8217;0&#8242;,&#8217;1&#8242;) NOT NULL default &#8217;0&#8242;,</p>
<p>Password varchar(64) NOT NULL default &#187;,</p>
<p>Uid varchar(11) NOT NULL default &#8217;-1&#8242;,</p>
<p>Gid varchar(11) NOT NULL default &#8217;-1&#8242;,</p>
<p>Dir varchar(128) NOT NULL default &#187;,</p>
<p>ULBandwidth smallint(5) NOT NULL default &#8217;0&#8242;,</p>
<p>DLBandwidth smallint(5) NOT NULL default &#8217;0&#8242;,</p>
<p>comment tinytext NOT NULL,</p>
<p>ipaccess varchar(15) NOT NULL default &#8217;*',</p>
<p>QuotaSize smallint(5) NOT NULL default &#8217;0&#8242;,</p>
<p>QuotaFiles int(11) NOT NULL default 0,</p>
<p>PRIMARY KEY (User),</p>
<p>UNIQUE KEY User (User)</p>
<p>) TYPE=MyISAM;</p>
<p>quit;</p></div>
</div>
<p>Как вы возможно заметили, командой quit; мы завершаем работу с MySQL.</p>
<p>Кстати, (я предполагаю, что имя вашего ftp сервера server1.example.com) вы можете получить доступ используя phpMyAdmin по адресу <a href="http://web.archive.org/web/20080307232943/http://server1.example.com/phpmyadmin/">http://server1.example.com/phpmyadmin/</a> (вы также можете использовать IP адрес вместо server1.example.com) и войти как пользователь pureftpd. После этого вы можете посмотреть на базу данных. Позже вы сможете использовать phpMyAdmin для администрирования вашего PureFTPd сервера.</p>
<h2>Конфигурация PureFTPd</h2>
<p>Отредактируйте /etc/pure-ftpd/db/mysql.conf. Это делается примерно так:</p>
<div>
<div>cp /etc/pure-ftpd/db/mysql.conf /etc/pure-ftpd/db/mysql.conf_orig</p>
<p>cat /dev/null &amp;gt; /etc/pure-ftpd/db/mysql.conf</p>
<p>vi /etc/pure-ftpd/db/mysql.conf</p>
<p>MYSQLSocket      /var/run/mysqld/mysqld.sock</p>
<p>#MYSQLServer     localhost</p>
<p>#MYSQLPort       3306</p>
<p>MYSQLUser       pureftpd</p>
<p>MYSQLPassword   ftpdpass</p>
<p>MYSQLDatabase   pureftpd</p>
<p>#MYSQLCrypt md5, cleartext, crypt() or password() - md5 is VERY RECOMMENDABLE uppon cleartext</p>
<p>MYSQLCrypt      md5</p>
<p>MYSQLGetPW      SELECT Password FROM ftpd WHERE User=&#187;\L&#187; AND status=&#187;1&#8243; AND (ipaccess = &#187;*&#187; OR ipaccess LIKE &#187;\R&#187;)</p>
<p>MYSQLGetUID     SELECT Uid FROM ftpd WHERE User=&#187;\L&#187; AND status=&#187;1&#8243; AND (ipaccess = &#187;*&#187; OR ipaccess LIKE &#187;\R&#187;)</p>
<p>MYSQLGetGID     SELECT Gid FROM ftpd WHERE User=&#187;\L&#187;AND status=&#187;1&#8243; AND (ipaccess = &#187;*&#187; OR ipaccess LIKE &#187;\R&#187;)</p>
<p>MYSQLGetDir     SELECT Dir FROM ftpd WHERE User=&#187;\L&#187;AND status=&#187;1&#8243; AND (ipaccess = &#187;*&#187; OR ipaccess LIKE &#187;\R&#187;)</p>
<p>MySQLGetBandwidthUL SELECT ULBandwidth FROM ftpd WHERE User=&#187;\L&#187;AND status=&#187;1&#8243; AND (ipaccess = &#187;*&#187; OR ipaccess LIKE &#187;\R&#187;)</p>
<p>MySQLGetBandwidthDL SELECT DLBandwidth FROM ftpd WHERE User=&#187;\L&#187;AND status=&#187;1&#8243; AND (ipaccess = &#187;*&#187; OR ipaccess LIKE &#187;\R&#187;)</p>
<p>MySQLGetQTASZ   SELECT QuotaSize FROM ftpd WHERE User=&#187;\L&#187;AND status=&#187;1&#8243; AND (ipaccess = &#187;*&#187; OR ipaccess LIKE &#187;\R&#187;)</p>
<p>MySQLGetQTAFS   SELECT QuotaFiles FROM ftpd WHERE User=&#187;\L&#187;AND status=&#187;1&#8243; AND (ipaccess = &#187;*&#187; OR ipaccess LIKE &#187;\R&#187;)</p></div>
</div>
<p>Убедитесь, что заменили ftpdpass настоящим паролем пользователя pureftpd в строке MYSQLPassword!! Убедитесь, что используете md5 как MYSQLCrypt метод, что означает, что мы запоминаем пароли пользователей как MD5 строки, что гораздо безопаснее использования обычного текста!</p>
<p>Затем создайте файл /etc/pure-ftpd/conf/ChrootEveryone, содержащий строчку yes:</p>
<div>
<div>echo &#187;yes&#187; &amp;gt; /etc/pure-ftpd/conf/ChrootEveryone</div>
</div>
<p>Это заставит PureFTPd делать chroot для каждого пользователя в его домашний каталог и он не сможет простматривать файлы и папке вне домашнего каталога.</p>
<p>Также создайте файл /etc/pure-ftpd/conf/CreateHomeDir, также содержащий строку yes:</p>
<div>
<div>echo &#187;yes&#187; &amp;gt; /etc/pure-ftpd/conf/CreateHomeDir</div>
</div>
<p>Это заставит PureFTPd создавать домашний каталог пользователя если его не существует.</p>
<p>Теперь нам надо сконфигурировать PureFTPd как самостоятельный демон (пока он конторолируется inetd). Чтобы это сделать, мы откроем /etc/default/pure-ftpd-common и изменим значение параметра STANDALONE_OR_INETD на standalone:</p>
<div>
<div>vi /etc/default/pure-ftpd-common</p>
<p># Configuration for pure-ftpd</p>
<p># (this file is sourced by /bin/sh, edit accordingly)</p>
<p># STANDALONE_OR_INETD</p>
<p># valid values are &#187;standalone&#187; and &#187;inetd&#187;.</p>
<p># Any change here overrides the setting in debconf.</p>
<p>STANDALONE_OR_INETD=standalone</p>
<p># VIRTUALCHROOT:</p>
<p># whether to use binary with virtualchroot support</p>
<p># valid values are &#187;true&#187; or &#187;false&#187;</p>
<p># Any change here overrides the setting in debconf.</p>
<p>VIRTUALCHROOT=false</p>
<p># UPLOADSCRIPT: if this is set and the daemon is run in standalone mode,</p>
<p># pure-uploadscript will also be run to spawn the program given below</p>
<p># for handling uploads. see /usr/share/doc/pure-ftpd/README.gz or</p>
<p># pure-uploadscript(8)</p>
<p># example: UPLOADSCRIPT=/usr/local/sbin/uploadhandler.pl</p>
<p>UPLOADSCRIPT=</p>
<p># if set, pure-uploadscript will spawn $UPLOADSCRIPT running as the</p>
<p># given uid and gid</p>
<p>UPLOADUID=</p>
<p>UPLOADGID=</p></div>
</div>
<p>Затем изменим /etc/inetd.conf, раскомментировав строку ftp:</p>
<div>
<div>vi /etc/inetd.conf</p>
<p>[...]</p>
<p>#ftp     stream  tcp     nowait  root    /usr/sbin/tcpd /usr/sbin/pure-ftpd-wrapper</p>
<p>[...]</p></div>
</div>
<p>После этого перезапустим Inetd и PureFTPd:</p>
<div>
<div>/etc/init.d/openbsd-inetd restart</p>
<p>/etc/init.d/pure-ftpd-mysql restart</p></div>
</div>
<h2>Заполнение базы данных и тесты</h2>
<p>Для заполнения базы данных вы можете использовать командный интерпритатор MySQL:</p>
<div>
<div>mysql -u root -p</p>
<p>USE pureftpd;</p></div>
</div>
<p>Теперь создадим пользователя exampleuser со статусом 1 (что означает, что его ftp аккаунт &#8212; активный), пароль secret (который будет сохранен в заштфрованном виде, используя MySQL функцию MD5), UID и GID 2001 (используйте userid и groupid пользователя/группы, которых вы создали в конце 2 части!), домашний каталог /home/www.example.com, лимит входяшей/исходящей пропускной способности 100 KБ/с (килобайт в секунду), и квоту в 50 MБ:</p>
<div>
<div>INSERT INTO `ftpd` (`User`, `status`, `Password`, `Uid`, `Gid`, `Dir`, `ULBandwidth`, `DLBandwidth`, `comment`, `ipaccess`, `QuotaSize`, `QuotaFiles`)VALUES (&#8216;exampleuser&#8217;, &#8217;1&#8242;, MD5(&#8216;secret&#8217;), &#8217;2001&#8242;, &#8217;2001&#8242;, &#8217;/home/www.example.com&#8217;, &#8217;100&#8242;, &#8217;100&#8242;, &#187;, &#8217;*', &#8217;50&#8242;, &#8217;0&#8242;);</p>
<p>quit;</p></div>
</div>
<p>Теперь откройте FTP клиент на своей рабочей станцие (что-нибудь вроде WS_FTP или SmartFTP если у вас Windows или gFTP в Linux) и попробуйте подключиться. В качестве имени используйте server1.example.com (или соответствующий IP адрес), имя пользователя exampleuser и пароль secret.</p>
<p>Если вам удалось подключиться &#8212; поздравляю! Если нет, то что-то пошло не так.</p>
<p>Теперь, если вы выполните</p>
<div>
<div>ls -l /home</div>
</div>
<p>вы должны увидеть, что директория /home/www.example.com (домашний каталог пользователя exampleuser) была автоматически создана, и принадлежит ftpuser и ftpgroup (Пользователь/группа, которых мы создали в конце 2 части):</p>
<div>
<div>server1:/etc/default# ls -l /home</p>
<p>total 8</p>
<p>drwxr-xr-x 2 administrator administrator 4096 2007-04-23 14:25 administrator</p>
<p>drwxr-xr-x 2 ftpuser       ftpgroup      4096 2007-04-23 17:26 www.example.com</p></div>
</div>
<h2>Администрирование базы данных</h2>
<p>Для большинства людей легче всего использовать графическую оболочку для MySQL; однако так же можете использовать phpMyAdmin (в этом примере по адресу<a href="http://web.archive.org/web/20080307232943/http://server1.example.com/phpmyadmin/">http://server1.example.com/phpmyadmin/</a>) для администрирования базы данных pureftpd.</p>
<p>Когда вы хотите создать нового пользователя, вам надо создать запись в таблице ftpd. Далее будут описаны все строки этой таблицы:</p>
<p>Таблица ftpd:</p>
<ul>
<li><strong>User</strong>: Имя виртуального пользователя PureFTPd.</li>
<li><strong>status</strong>: 0 или 1. 0 означает, что аккаунт отключен и пользователь не может подключится.</li>
<li><strong>Password</strong>: Пароль виртуального пользователя.</li>
<li><strong>UID</strong>: userid пользователя, который был создан во 2 части.</li>
<li><strong>GID</strong>: groupid пользователя, который был создан во 2 части.</li>
<li><strong>Dir</strong>: Домашний каталог виртуального пользователя PureFTPd.</li>
<li><strong>ULBandwidth</strong>: Скорость закачки для вирткального пользователя и КБ/с. 0 &#8212; не ограничена.</li>
<li><strong>DLBandwidth</strong>: Скорость скачки для вирткального пользователя и КБ/с. 0 &#8212; не ограничена.</li>
<li><strong>comment</strong>: Комментарии. Поле можно оставить пустым.</li>
<li><strong>ipaccess</strong>: IP адрес, с которого подключаться с данным аккаунтом.</li>
<li><strong>QuotaSize</strong>: Квота в МБ.</li>
<li><strong>QuotaFiles</strong>: Количество файлов, которое можно сохранять.0 &#8212; не ограничено.</li>
</ul>
<h2>Анонимный FTP</h2>
<p>Если вы хотите создать анонимный аккаунт (аккаунт, с которым любой может подключаться без пароля), вам надо сделать что-то вроде этого:<br />
Сначала надо создать пользователя ftp (с домашним каталогом /home/ftp) и группу ftp:</p>
<div>
<div>groupadd ftp</p>
<p>useradd -s /bin/false -d /home/ftp -m -c &#187;anonymous ftp&#187; -g ftp ftp</p></div>
</div>
<p>Затем создать файл /etc/pure-ftpd/conf/NoAnonymous, содержайщий строку no:</p>
<div>
<div>echo &#187;no&#187; &amp;gt; /etc/pure-ftpd/conf/NoAnonymous</div>
</div>
<p>После этого PureFTPd позволит использовать анонимный вход.</p>
<p>перезапустите PureFTPd:</p>
<div>
<div>/etc/init.d/pure-ftpd-mysql restart</div>
</div>
<p>Теперь создадим директорию /home/ftp/incoming, в которую позволим анонимным пользователям закачивать файлы. Дадим директории /home/ftp/incoming права 311 и пользователи смогут закачивать файлы, но не смогут их видеть или скачивать. Директория /home/ftp будет иметь права 555, что позволит видеть и скачивать файлы:</p>
<div>
<div>cd /home/ftp</p>
<p>mkdir incoming</p>
<p>chown ftp:nogroup incoming/</p>
<p>chmod 311 incoming/</p>
<p>cd ../</p>
<p>chmod 555 ftp/</p></div>
</div>
<p>Теперь анонимные пользователи могут подключаться, скачивать файлы из /home/ftp и закачивать в /home/ftp/incoming.</p>
<h2>Ссылки</h2>
<ul>
<ul>
<li>PureFTPd: <a href="http://web.archive.org/web/20080307232943/http://www.pureftpd.org%3C/li%3E%3Cp%3E">http://www.pureftpd.org</a></li>
</ul>
</ul>
<p>&nbsp;</p>
<ul>
<ul>
<li>MySQL: <a href="http://web.archive.org/web/20080307232943/http://www.mysql.com%3C/li%3E%3Cp%3E">http://www.mysql.com</a></li>
</ul>
</ul>
<p>&nbsp;</p>
<ul>
<ul>
<li>phpMyAdmin: <a href="http://web.archive.org/web/20080307232943/http://www.phpmyadmin.net%3C/li%3E%3Cp%3E">http://www.phpmyadmin.net</a></li>
</ul>
</ul>
<p>&nbsp;</p>
<ul>
<li>Debian: <a href="http://web.archive.org/web/20080307232943/http://www.debian.org%3C/li%3E%3Cp%3E">http://www.debian.org</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://codesources.net/pureftpd-mysql-v-debian/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Apache 2: Исправляем ошибку 413 Request entity too large</title>
		<link>http://codesources.net/apache-2-ispravlyaem-oshibku-413-request-entity-too-large/</link>
		<comments>http://codesources.net/apache-2-ispravlyaem-oshibku-413-request-entity-too-large/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:33:47 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Apache]]></category>
		<category><![CDATA[Unix]]></category>

		<guid isPermaLink="false">http://codesources.net/?p=71</guid>
		<description><![CDATA[Решение ищется просто: Server:#cd /etc/apache2 Server:/etc/apache2# cat apache2.conf &#124;grep Limit SecRequestBodyLimit 131072 SecRequestBodyInMemoryLimit 131072 SecResponseBodyLimit 524288 Server:/etc/apache2# Нас интерсует как<br /><a class="more-link" href="http://codesources.net/apache-2-ispravlyaem-oshibku-413-request-entity-too-large/">keep reading</a>]]></description>
			<content:encoded><![CDATA[<p>Решение ищется просто:</p>
<div>Server:#cd /etc/apache2 Server:/etc/apache2# cat apache2.conf |grep Limit SecRequestBodyLimit 131072 SecRequestBodyInMemoryLimit 131072 SecResponseBodyLimit 524288 Server:/etc/apache2#</div>
<p>Нас интерсует как раз SecRequestBodyLimit, открываем конфигурационный файл Apache2</p>
<div>server:/etc/apache2# nano apache2.conf</div>
<p>Выставляем свое значение для SecRequestBodyLimit , к примеру, на класс выше:</p>
<div>SecRequestBodyLimit 1310720</div>
<p>Сохраняем файл, перезагружаем Apache2:</p>
<div>server:/etc/apache2# /etc/init.d/apache2 restart</div>
<p>Все.</p>
]]></content:encoded>
			<wfw:commentRss>http://codesources.net/apache-2-ispravlyaem-oshibku-413-request-entity-too-large/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Delphi: делаем скриншот сайта</title>
		<link>http://codesources.net/delphi-delaem-skrinshot-sajta/</link>
		<comments>http://codesources.net/delphi-delaem-skrinshot-sajta/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:28:57 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Интернет]]></category>
		<category><![CDATA[Интерфейс]]></category>

		<guid isPermaLink="false">http://codesources.net/?p=68</guid>
		<description><![CDATA[Данная функция позволяет сделать скриншот сайта с помощью IWebBrower procedure generateJPEGfromBrowser(browser: iWebBrowser2; jpegFQFilename: String; srcHeight: Integer; srcWidth: Integer; tarHeight: Integer; tarWidth: Integer); var sourceDrawRect : TRect;<br /><a class="more-link" href="http://codesources.net/delphi-delaem-skrinshot-sajta/">keep reading</a>]]></description>
			<content:encoded><![CDATA[<p>Данная функция позволяет сделать скриншот сайта с помощью IWebBrower</p>
<div>
<div>procedure generateJPEGfromBrowser(browser: iWebBrowser2; jpegFQFilename: String;<br />
srcHeight: Integer; srcWidth: Integer; tarHeight: Integer; tarWidth: Integer);<br />
var<br />
sourceDrawRect : TRect;<br />
targetDrawRect: TRect;<br />
sourceBitmap: TBitmap;<br />
targetBitmap: TBitmap;<br />
jpeg: TJPEGImage;<br />
viewObject: IViewObject;<br />
begin<br />
sourceBitmap := TBitmap.Create ;<br />
targetBitmap := TBitmap.Create ;<br />
jpeg := TJPEGImage.Create ;<br />
try<br />
try<br />
sourceDrawRect := Rect(0,0, srcWidth , srcHeight );<br />
sourceBitmap.Width :=  srcWidth ;<br />
sourceBitmap.Height :=  srcHeight ;</p>
<p>viewObject := browser as IViewObject;</p>
<p>if viewObject = nil then<br />
Exit;</p>
<p>OleCheck(viewObject.Draw(DVASPECT_CONTENT, 1, nil, nil, self.Handle,<br />
sourceBitmap.Canvas.Handle, @sourceDrawRect, nil, nil, 0));</p>
<p>// Resize Bitmap<br />
targetDrawRect := Rect(0,0, tarWidth, tarHeight);<br />
targetBitmap.Height := tarHeight;<br />
targetBitmap.Width := tarWidth;<br />
targetBitmap.Canvas.StretchDraw(targetDrawRect, sourceBitmap);</p>
<p>// Create JPEG from Bitmap and save it<br />
jpeg.Assign(targetBitmap) ;<br />
makeFileWriteable(jpegFQFilename);<br />
jpeg.SaveToFile (jpegFQFilename);<br />
finally<br />
jpeg.free;<br />
sourceBitmap.free ;<br />
targetBitmap.free;<br />
end;<br />
except<br />
// errors<br />
end;<br />
end;</p></div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://codesources.net/delphi-delaem-skrinshot-sajta/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Delphi: Открыть сайт в Internet Explorer</title>
		<link>http://codesources.net/delphi-otkryt-sajt-v-internet-explorer/</link>
		<comments>http://codesources.net/delphi-otkryt-sajt-v-internet-explorer/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:27:52 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Интернет]]></category>

		<guid isPermaLink="false">http://codesources.net/?p=66</guid>
		<description><![CDATA[Данный пример показывает как запустить Internet Explorer с открыванием какого-либо URL. Для работы требуется модуль ShellAPI ShellExecute(TForm(Owner).Handle,nil,PChar(&#8216;http://codesources.net/&#8217;), nil, nil,SW_SHOWNORMAL);]]></description>
			<content:encoded><![CDATA[<p>Данный пример показывает как запустить Internet Explorer с открыванием какого-либо URL. Для работы требуется модуль ShellAPI</p>
<div>
<div>ShellExecute(TForm(Owner).Handle,nil,PChar(&#8216;http://codesources.net/&#8217;), nil, nil,SW_SHOWNORMAL);</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://codesources.net/delphi-otkryt-sajt-v-internet-explorer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Named Pipes</title>
		<link>http://codesources.net/named-pipes/</link>
		<comments>http://codesources.net/named-pipes/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:25:57 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Интернет]]></category>
		<category><![CDATA[Сеть]]></category>

		<guid isPermaLink="false">http://codesources.net/?p=63</guid>
		<description><![CDATA[Именованные каналы (Named Pipes) &#8212; это объекты ядра, являющиеся средством межпроцессной коммуникации между сервером канала и одним или несколькими клиентами<br /><a class="more-link" href="http://codesources.net/named-pipes/">keep reading</a>]]></description>
			<content:encoded><![CDATA[<p align="justify">Именованные каналы (Named Pipes) &#8212; это объекты ядра, являющиеся средством межпроцессной коммуникации между сервером канала и одним или несколькими клиентами канала.</p>
<p align="justify">
<p align="justify">Сервером канала называется процесс, создающий именованный канал.</p>
<p align="justify">Клиентом канала называется процесс, подключающийся к созданному именованному каналу.</p>
<p align="justify">От других аналогичных объектов именованные каналы отличает гарантированная доставка сообщений, возможность асинхронного ввода/вывода, возможность коммуникации между процессами на разных компьютерах в локальной вычислительной сети и относительная простота использования.</p>
<p align="justify">По своему назначению они похожи на каналы операционной системы UNIX.</p>
<p align="justify">При создании именованного канала ему назначается уникальное имя, определяется максимальное количество одновременных соединений с клиентами канала и режим работы канала (должен ли канал быть односторонним или двусторонним (дуплексным), ведется ли передача пакетами или потоком байтов. При передаче пакетами данные одной операции записи отделяются в буфере канала от данных другой операции записи).</p>
<p align="justify">Базовым объектом для реализации именованных каналов служит объект &#171;файл&#187;, поэтому для посылки и приема сообщений по именованным каналам используются те же самые функции Windows API, что и при работы с файлами (ReadFile, WriteFile).</p>
<p align="justify">Для каждого процесса-клиента канала создается свой экземпляр канала, с собственными буферами и дескрипторами (handles) и с собственным механизмом передачи данных, не влияющим на остальные экземпляры.</p>
<p align="justify">Экземпляры одного канала имеют общее имя, указанное при создании, сервер назначает имя канала в соответствии с универсальными правилами именования (Universal Naming Convention, UNC), которые обеспечивают независимый от протоколов способ идентификации каналов в Windows-сетях [1].</p>
<p align="justify">Именованные каналы, также как и файлы, наследуют стандартную защиту объектов Windows, что позволяет разграничить участников коммуникации и обеспечить запрет несанкционированного доступа к каналу.</p>
<p align="justify">Создание именованных каналов возможно только в NT-системах, подключение к созданному каналу возможно как в NT-системах, так и в Win9x. Кроме того, API работы с каналами в Win9x не поддерживает асинхронных операций ввода/вывода.</p>
<p align="justify">Именованные каналы широко используются внутри самой системы. Например, взаимодействие менеджера сервисов с самими сервисами осуществляется через несколько именованных каналов. Для связи с сервисами RunAs, с планировщиком событий и с сервером локальной аутентификации также используются именованные каналы.</p>
<p align="justify">Именованные каналы являются наиболее простым способом организации связи между сервисами и пользовательскими приложениями, нуждающимися в такой связи.</p>
<p align="justify">Одним из полезных (и довольно уникальных) свойств именованного канала является возможность сервера заменять права своей учетной записи правами учетной записи клиента, соединившегося с каналом. Эта возможность служит преимущественно для ограничения прав сервера при выполнении операций доступа к различным объектам системы.</p>
<p align="justify">Для работы с именованными каналами Windows API предоставляет следующие функции:</p>
<table>
<tbody>
<tr>
<td>CreateNamedPipe</td>
<td>Создание именованного канала или нового экземпляра канала. Функция доступна только серверу.</td>
</tr>
<tr>
<td>ConnectNamedPipe или CreateFile</td>
<td>Подключение к экземпляру именованного канала со стороны клиента. Функция доступна только клиенту.</td>
</tr>
<tr>
<td>WaitNamedPipe</td>
<td>Ожидание клиентом появления свободного экземпляра именованного канала для подключения к нему.</td>
</tr>
<tr>
<td>ConnectNamedPipe</td>
<td>Ожидание сервером подключения клиента к экземпляру именованного канала.</td>
</tr>
<tr>
<td>ReadFile, ReadFileEx</td>
<td>Чтение данных из именованного канала. Функция доступна как клиенту, так и серверу.</td>
</tr>
<tr>
<td>WriteFile, WriteFileEx</td>
<td>Запись данных в именованный канал. Функция доступна как клиенту, так и серверу.</td>
</tr>
<tr>
<td>PeekNamedPipe</td>
<td>Чтение данных из именованного канала без удаления прочитанных данных из буфера канала. Функция доступна как клиенту, так и серверу.</td>
</tr>
<tr>
<td>TransactNamedPipe</td>
<td>Запись и чтение из именованного канала одной операцией. Функция доступна как клиенту, так и серверу.</td>
</tr>
<tr>
<td>DisconnectNamedPipe</td>
<td>Отсоединение сервера от экземпляра именованного канала.</td>
</tr>
<tr>
<td>GetNamedPipeInfo</td>
<td>Получение информации об именованном канале.</td>
</tr>
<tr>
<td>GetNamedPipeHandleState</td>
<td>Получение текущего режима работы именованного канала и количества созданных экземпляров канала.</td>
</tr>
<tr>
<td>SetNamedPipeHandleState</td>
<td>Установка текущего режима работы именованного канала.</td>
</tr>
<tr>
<td>CloseHandle</td>
<td>Закрытие дескриптора экземпляра именованного канала, освобождение связанных с объектом ресурсов.</td>
</tr>
<tr>
<td>FlushFileBuffers</td>
<td>Сброс данных из кэша в буфер канала.</td>
</tr>
</tbody>
</table>
<p align="justify">Рассмотрим алгоритм работы сервера двустороннего именованного канала, обслуживающего одновременно нескольких клиентов с использованием асинхронного ввода-вывода. Работа данного сервера заключается в пересылке сообщений, полученных от каждого клиента всем клиентам, подключенным к каналу (некий аналог чата).</p>
<p align="justify">После инициализации, процесс сервера запускает поток, обслуживающий клиентское соединение. Поток создает именованный канал, используя функцию CreateNamedPipe, указывает максимальное количество экземпляров канала (равное максимально возможному количеству одновременно обслуживаемых клиентов) и режимы работы канала, одновременно с созданием канала создается первый его экземпляр.</p>
<p align="justify">Затем поток сервера, обслуживающий этот экземпляр именованного канала, вызывает функцию ConnectNamedPipe для ожидания клиентского подключения.</p>
<p align="justify">После того, как клиент подключился к экземпляру именованного канала, сервер инициализирует структуры для обмена данными с клиентом, читает из канала имя подсоединившегося клиента, регистрирует его и запускает копию этого же потока для обслуживания следующего клиентского подключения. Далее, поток обслуживания экземпляра канала входит в цикл чтения канала и рассылки прочитанных сообщений всем подключенным клиентам до тех пор, пока соединение с клиентом не разорвется.</p>
<p align="justify">После разрыва соединения поток отключается от экземпляра канала, чтобы освободить ресурсы, выделенные системой под обслуживание этого экземпляра, и завершает свою работу.</p>
<p align="justify">Последующие потоки при вызове функции CreateNamedPipe создают дополнительные экземпляры канала, при этом параметры, задающие режимы работы канала функцией игнорируются.</p>
<p align="justify">Алгоритм работы клиента следующий: После запуска клиент запрашивает у пользователя имя сервера и свое имя для идентификации, подключается к экземпляру канала вызовом функции CreateFile с указанием имени канала, регистрируется на сервере с помощью посылки в канал своего имени и запускает поток асинхронного чтения сообщений из канала. Основной поток принимает сообщения пользователя и передает их в канал серверу. Поток чтения работает до тех пор, пока не разорвется соединение с сервером, или пока пользователь не завершит клиентский процесс.</p>
<p align="justify">Рассмотрим реализацию потока сервера, обслуживающего экземпляр именованного канала:</p>
<div>
<div>const<br />
MAX_PIPE_INSTANCES = 100;<br />
NAME_SIZE = 25;<br />
LINE_LEN = 80;</p>
<p>{ Описание клиента, подключенного к каналу }<br />
type<br />
WRTHANDLE = packed record<br />
hPipe: THANDLE;<br />
hEvent: THANDLE;<br />
overLap: OVERLAPPED;<br />
Live: LongBool;<br />
Name: array[0..NAME_SIZE] of WideChar;<br />
end;</p>
<p>var<br />
ClientCount: Integer = 0;<br />
Clients: array[1..MAX_PIPE_INSTANCES] of WRTHANDLE;<br />
Wnd: HWND;</p>
<p>procedure ServerProc (Param: Pointer); stdcall;<br />
type<br />
PHWND = ^HWND;<br />
const<br />
IN_BUF_SIZE = 1000;<br />
OUT_BUF_SIZE = 1000;<br />
TIME_OUT = 0;<br />
MAX_READ = 1000*Sizeof(WideChar);<br />
var<br />
WindowHandle: HWND;<br />
Dummy: ULONG;<br />
hPipe: THANDLE;<br />
inBuf: array[0..IN_BUF_SIZE] of WideChar;  // Буфер чтения.<br />
bytesRead: DWORD;<br />
bytesTransRd: DWORD;<br />
rc: Boolean;<br />
ClientIndex: Integer;<br />
LastError: DWORD;<br />
ExitLoop: Boolean;</p>
<p>OverLapWrt: OVERLAPPED;<br />
hEventWrt: THANDLE;<br />
OverLapRd: OVERLAPPED;<br />
hEventRd: THANDLE;<br />
pSD: PSECURITY_DESCRIPTOR;<br />
sa: SECURITY_ATTRIBUTES;<br />
begin<br />
WindowHandle := PHWND(Param)^;<br />
inBuf[0] := #0;<br />
ExitLoop := false;<br />
lastError := 0;<br />
// Создать пустой дескриптор безопасности, позволяющий всем писать в канал.<br />
// Предупреждение: Указание nil в качестве последнего параметра функции<br />
// CreateNamedPipe() означает, что все клиенты, подсоединившиеся к каналу<br />
// будут иметь те же атрибуты безопасности, что и пользователь, чья учетная<br />
// запись использовалась при создании серверной стороны канала.<br />
pSD := PSECURITY_DESCRIPTOR(LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));<br />
if not Assigned(pSD) then begin<br />
MessageBoxW(WindowHandle, &#8217;Error allocation memory for SD&#8217; ,<br />
&#8216;Debug: ServerProc()&#8217; , MB_OK);<br />
Exit;<br />
end;<br />
if not InitializeSecurityDescriptor (pSD,<br />
SECURITY_DESCRIPTOR_REVISION) then begin<br />
ShowLastErrorMessage(WindowHandle,<br />
&#8216;Debug: ServerProc(): InitializeSecurityDescriptor&#8217; );<br />
LocalFree(HLOCAL(pSD));<br />
Exit;<br />
end;<br />
// Добавить NULL ACL к дескриптору безопасности<br />
if not SetSecurityDescriptorDacl(pSD, true, nil, false) then begin<br />
ShowLastErrorMessage(WindowHandle,<br />
&#8216;Debug: ServerProc():SetSecurityDescriptorDacl&#8217; );<br />
LocalFree(HLOCAL(pSD));<br />
Exit;<br />
end;<br />
sa.nLength := sizeof(sa);<br />
sa.lpSecurityDescriptor := pSD;<br />
sa.bInheritHandle := true;<br />
// Создать серверную часть канала на локальной машине<br />
hPipe := CreateNamedPipeW ( &#8217;\.PIPEtest&#8217; ,  // Имя канала = &#8216;test&#8217;.<br />
PIPE_ACCESS_DUPLEX or  // Двусторонний канал<br />
FILE_FLAG_OVERLAPPED,  // Асинхронный ввод-вывод<br />
PIPE_WAIT or  // Ожидать сообщений<br />
PIPE_READMODE_MESSAGE or  // Обмен в канале производится пакетами<br />
PIPE_TYPE_MESSAGE,<br />
MAX_PIPE_INSTANCES,  // Максимальное числе экземпляров канала.<br />
OUT_BUF_SIZE*SizeOf(WideChar),  // Размеры буферов чтения/записи.<br />
IN_BUF_SIZE*SizeOf(WideChar),<br />
TIME_OUT,  // Тайм-аут.<br />
@sa);  // Атрибуты безопасности.<br />
if hPipe = INVALID_HANDLE_VALUE then begin<br />
ShowLastErrorMessage(WindowHandle, &#8217;Debug: ServerProc():CreateNamedPipeW&#8217; );<br />
Exit;<br />
end;<br />
// Ожидаем подключения клиента.<br />
ConnectNamedPipe(hPipe, nil);<br />
// Создаем событие ожидания завершения записи в канал.<br />
hEventWrt := CreateEventW (nil, true, false, nil);<br />
FillChar(OverLapWrt, sizeof(OVERLAPPED), 0);<br />
OverLapWrt.hEvent := hEventWrt;<br />
// Создаем событие ожидания завершения чтения из канала.<br />
hEventRd := CreateEventW (nil, true, false, nil);<br />
FillChar(OverLapRd, sizeof(OVERLAPPED), 0);<br />
OverLapRd.hEvent := hEventRd;<br />
// Для подсоединившегося клиента заполним его описание<br />
Inc(ClientCount);<br />
ClientIndex := ClientCount;<br />
Clients[ClientIndex].hPipe := hPipe;<br />
Clients[ClientIndex].Live := true;<br />
Clients[ClientIndex].OverLap := OverLapWrt;<br />
Clients[ClientIndex].hEvent := hEventWrt;<br />
// первым сообщением от клиента должно быть его имя<br />
rc := ReadFile (hPipe, inBuf, MAX_READ, bytesRead, @OverLapRd);<br />
if not rc then<br />
lastError := GetLastError;<br />
if lastError = ERROR_IO_PENDING then  // Ожидаем завершения ввода-вывода<br />
WaitForSingleObject (hEventRd, INFINITE);<br />
// Запоминаем имя текущего клиента.<br />
lstrcpyw (Clients[ClientIndex].Name, inBuf);<br />
// Запускаем новый поток для ожидания нового клиента<br />
CreateThread (nil, 0, @ServerProc, Param, 0, Dummy);  //Поток выполняется сразу<br />
// Посылка пустой строки вызовет обновление списка клиентов и его перерисовку<br />
TellAll( &#187; );<br />
// Читаем сообщения от этого клиента и передаем его всем подключенным клиентам<br />
repeat<br />
rc := ReadFile (hPipe, inBuf, MAX_READ, bytesRead, @OverLapRd);<br />
// Проверяем три вида ошибки: IO_PENDING (ждем завершения операции)<br />
// При BROKEN_PIPE (клиент или сервер умер), выход из цикла и завершение<br />
// обслуживания клиента.<br />
// При остальных ошибках выдаем сообщение, помечаем факт смерти клиента<br />
// и завершаем его обслуживание, выходя из цикла чтения.<br />
if not rc then begin<br />
lastError := GetLastError;<br />
case lastError of<br />
ERROR_IO_PENDING:  // Ожидаем завершения операции<br />
WaitForSingleObject (hEventRd, INFINITE);<br />
ERROR_BROKEN_PIPE:  // Экземпляр канала сломался, завершаем обслуживание.<br />
ExitLoop := true;<br />
else<br />
// Выдаем сообщение о нештатной ошибке и завершаем обслуживание клиента.<br />
begin<br />
ShowLastErrorMessage(WindowHandle, &#8217;Debug: ServerProc():ReadFile&#8217; );<br />
ExitLoop := true;<br />
end;<br />
end;<br />
end;<br />
if not ExitLoop then begin<br />
GetOverlappedResult (hPipe, OverLapRd, bytesTransRd, false);<br />
// Пересылаем сообщение всем клиентам<br />
if bytesTransRd &amp;lt;&amp;gt; 0 then<br />
TellAll(inBuf)<br />
else<br />
TellAll( &#187; );<br />
end;<br />
until ExitLoop;<br />
Clients[ClientIndex].Live := false;  // При выходе из цикла чтения клиент мертв<br />
CloseHandle (hPipe);<br />
CloseHandle (hEventRd);<br />
CloseHandle (hEventWrt);<br />
DisconnectNamedPipe (hPipe);  // Разрушаем экземпляр канала<br />
ExitThread(0);  // Завершаем обслуживающий поток.<br />
end;</p></div>
</div>
<p>и процедуры рассылки сообщения клиентам:</p>
<div>
<div>procedure TellAll (const Message: PWideChar);</p>
<p>var<br />
I: Integer;<br />
BytesWritten: DWORD;<br />
rc: Boolean;<br />
lastError: DWORD;<br />
MsgLength: DWORD;<br />
begin<br />
//передать сообщение всем живым клиентам в списке.<br />
for I:=1 to ClientCount do<br />
if Clients[I].Live then begin<br />
MsgLength := lstrlenW(Message) * SizeOf(WideChar);<br />
rc := WriteFile (Clients[I].hPipe, Message^, MsgLength, bytesWritten,<br />
@Clients[I].overLap);<br />
// Проверка на три вида ошибки: IO_PENDING, NO_DATA и остальные.<br />
// Для случая IO_PENDING ожидать завершения асинхронного ввода-вывода<br />
// на событии клиента, во всех остальных случаях, кроме NO_DATA<br />
// считать клиента умершим и отметить факт его смерти в описании клиента.<br />
if not rc then begin<br />
lastError := GetLastError;<br />
if lastError = ERROR_IO_PENDING then  //Ждем завершения операции<br />
WaitForSingleObject (Clients[i].hEvent, INFINITE)<br />
else begin<br />
if lastError &amp;lt;&amp;gt; ERROR_NO_DATA then  //Клиент умер по причине lastError<br />
//TODO: Указывать имя покойника<br />
ShowLastErrorMessage (Wnd, &#8217;TellAll:&#8217; , lastError);<br />
//TODO: рассылать широковещательное сообщение об уходе?<br />
Clients[i].Live := false;</p>
<p>end;<br />
end;<br />
end;<br />
//Обновить окно с клиентами<br />
InvalidateRect(Wnd, nil, true);<br />
end;</p></div>
</div>
<p>Рассмотрим реализацию клиента, взаимодействующего с сервером именованного канала:</p>
<div>
<div>function ClientDlgProc (WindowHandle: HWND; Message: UINT;<br />
wParam, lParam: Cardinal): UINT; stdcall;</p>
<p>function TerminateDialog: UINT;<br />
begin<br />
CloseHandle (hPipe);<br />
CloseHandle (hEventWrt);<br />
EndDialog(WindowHandle, 1);<br />
Result := 1;<br />
end;</p>
<p>var<br />
retCode: DWORD;<br />
rc: Boolean;<br />
errorBuf: array[0..LINE_LEN] of WideChar;<br />
outBuf: array[0..OUT_BUF_SIZE] of WideChar;<br />
sendBuf: array[0..OUT_BUF_SIZE] of WideChar;<br />
bytesWritten: DWORD;<br />
Dummy: DWORD;<br />
fileName: array[0..LINE_LEN+NAME_SIZE+sizeof(WideChar)*2] of WideChar;<br />
AFileName: WideString;<br />
lastError: DWORD;<br />
APipeName: string;<br />
begin<br />
hWndClient := WindowHandle;<br />
errorBuf[0] := #0;<br />
outBuf[0] := #0;<br />
sendBuf[0] := #0;</p>
<p>case Message of<br />
WM_COMMAND:<br />
begin<br />
case LOWORD(wParam) of<br />
// После нажатия на кнопку Send получить текст для отправки серверу,<br />
// префиксировать его именем клиента и записать в канал.<br />
IDB_SEND:<br />
begin<br />
GetWindowTextW (GetDlgItem(WindowHandle, IDD_EDITWRITE), outBuf,<br />
MAX_WRITE);<br />
lstrcpyw(sendBuf, ClntName);<br />
lstrcatw(sendBuf, &#8217;:' );<br />
lstrcatw(sendBuf, outBuf);<br />
// Записать сообщение в канал<br />
rc := WriteFile (hPipe, sendBuf, MAX_WRITE, bytesWritten,<br />
@OverLapWrt);<br />
if not rc then begin<br />
lastError := GetLastError;<br />
// Если IO_PENDING, ждать завершения асинхронной операции записи<br />
if lastError = ERROR_IO_PENDING then<br />
WaitForSingleObject (hEventWrt, INFINITE);<br />
end;<br />
end;<br />
end;<br />
Result := 0;<br />
end;<br />
WM_INITCLIENT:<br />
// При инициализации создать диалог для получения имен сервера и клиента<br />
// Имя сервера, равное &#171;.&#187; означает, что сервер находится на том же<br />
// компьютере, что и клиент. Имя канала должно выглядеть как<br />
// &#8216;\.PIPE&#8217; для соединения с локальным сервером или<br />
// &#8216;\PIPE&#8217; для соединения с удаленным сервером<br />
// После соединения с каналом, отослать серверу свое имя для идентификации<br />
// и создать поток для чтения из канала.<br />
begin<br />
DialogBoxW (GetModuleHandle(nil), &#8217;InitDialog&#8217; , WindowHandle,<br />
@InitDlgProc);<br />
// Записать имя клиента в заголовок окна<br />
SetWindowTextW (WindowHandle, ClntName);<br />
APipeName:= Format( &#8217;\%sPIPEtest&#8217; , [WideCharToString(ShrName)]);<br />
AFileName:= StringToWideChar(APipeName, FileName, SizeOf(FileName));<br />
// Соединиться с сервером<br />
hPipe := CreateFileW (PWideChar(AFileName),<br />
GENERIC_WRITE or  // Доступ на чтение/запись<br />
GENERIC_READ,<br />
FILE_SHARE_READ or  // Разделенный доступ<br />
FILE_SHARE_WRITE,<br />
nil,<br />
OPEN_EXISTING,  // Канал должен существовать<br />
FILE_FLAG_OVERLAPPED,  // Использовать асинхронный ввод/вывод<br />
0);<br />
if hPipe = INVALID_HANDLE_VALUE then begin<br />
retCode := GetLastError;<br />
// Проверить попытку подключения к несуществующему каналу<br />
if (retCode = ERROR_SEEK_ON_DEVICE) or<br />
(retCode = ERROR_FILE_NOT_FOUND) then<br />
MessageBoxW (WindowHandle,<br />
&#8216;CANNOT FIND PIPE: Assure Server32 is started, check share name.&#8217; ,<br />
&#187; , MB_OK)<br />
else begin<br />
// Не удалось подключиться по другой причине<br />
MessageBoxW(WindowHandle, StringToWideChar(SysErrorMessage(retCode),<br />
errorBuf, SizeOf(errorBuf)),<br />
&#8216;Debug Window:CreateFileW&#8217; , MB_OK or MB_ICONINFORMATION<br />
or MB_APPLMODAL);<br />
end;<br />
EndDialog (WindowHandle, 0);  // Умереть, если не удалось соединиться<br />
Result := 0;<br />
Exit;<br />
end;<br />
hEventWrt := CreateEvent (nil, true, false, nil);<br />
OverLapWrt.hEvent := hEventWrt;<br />
// Сообщить серверу свое имя<br />
rc := WriteFile (hPipe, ClntName, MAX_WRITE, bytesWritten,<br />
@OverLapWrt);<br />
if not rc then  // Если IO_PENDING, ожидать звершения операции<br />
if GetLastError = ERROR_IO_PENDING then<br />
WaitForSingleObject (hEventWrt, INFINITE);<br />
// Создать поток чтения из канала.<br />
CreateThread (nil, 0, @ReadPipe, @hPipe, 0, Dummy);<br />
Result := 0;<br />
end;<br />
WM_INITDIALOG:<br />
// Послать сообщение в очередь, чтобы успел создаться диалог<br />
begin<br />
PostMessageW (WindowHandle, WM_INITCLIENT, 0, 0);<br />
Result := 0;<br />
end;<br />
WM_GO_AWAY:  // Завершение работы клиентской части из-за разрыва соединения<br />
// с сервером.<br />
Result := TerminateDialog;</p>
<p>WM_SYSCOMMAND:<br />
if (wParam and $FFF0) = SC_CLOSE then  // Если диалог закрывается<br />
// пользователем.<br />
Result := TerminateDialog<br />
else<br />
Result := 0;<br />
else<br />
Result := 0;<br />
end;<br />
end;</p></div>
</div>
<p>И клиентского потока асинхронного чтения данных из канала:</p>
<div>
<div>procedure ReadPipe (hPipe: PHANDLE); stdcall;<br />
var<br />
inBuf: array[0..IN_BUF_SIZE] of WideChar;<br />
bytesRead: DWORD;<br />
rc: Boolean;<br />
lastError: DWORD;<br />
hEventRd: THANDLE;<br />
OverLapRd: OVERLAPPED;<br />
bytesTrans: DWORD;<br />
begin<br />
inBuf[0] := #0;<br />
hEventRd := CreateEventW (nil, true, false, nil);<br />
FillChar (OverLapRd, sizeof(OVERLAPPED), 0);<br />
OverLapRd.hEvent := hEventRd;<br />
// Бесконечный цикл чтения из канала, до тех пор,пока не разорвется соединение<br />
// Чтение происходит асинхронно, с ожиданием по событию. После того, как сооб-<br />
// щение прочитано, оно помещается в элемент редактирования.<br />
while true do begin<br />
rc := ReadFile (hPipe^, inBuf, IN_BUF_SIZE*sizeof(WideChar), bytesRead,<br />
@OverLapRd);<br />
if not rc then begin<br />
lastError := GetLastError;<br />
// Проверка на три вида ошибки:<br />
// IO_PENDING (ожидать завершения операции), BROKEN_PIPE (выйти из цикла)<br />
// и остальные (выдать сообщение, выйти из цикла и умереть)<br />
if lastError = ERROR_IO_PENDING then begin<br />
WaitForSingleObject (hEventRd, INFINITE);<br />
end else begin<br />
if lastError = ERROR_BROKEN_PIPE then<br />
MessageBoxW (hWndClient,<br />
&#8216;The connection to this client has been broken.&#8217; , &#187; , MB_OK)<br />
else<br />
ShowLastErrorMessage(hWndClient,<br />
PAnsiChar( &#8217;Client: Debug():ReadFile&#8217; ));<br />
Break;<br />
end;<br />
end;<br />
GetOverlappedResult (hPipe^, OverLapRd, bytesTrans, false);<br />
inBuf[bytesTrans div SizeOf(WideChar)] := #0;  // Завершить полученную строку<br />
SendMessageW (GetDlgItem (hWndClient, IDD_EDITREAD), EM_REPLACESEL,<br />
0, LPARAM(@inBuf));<br />
// Перевести курсор на следующую строку в элементе редактирования <img src='http://codesources.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /><br />
SendMessageW (GetDlgItem (hWndClient, IDD_EDITREAD), EM_REPLACESEL,<br />
0, LPARAM(PWideChar(CrLf)));<br />
end;<br />
// Если соединение с каналом разорвано, завершить программу<br />
PostMessageW (hWndClient, WM_GO_AWAY, 0,0);<br />
ExitThread(0);</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://codesources.net/named-pipes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Загрузка HTML в IWebBrowser (Delphi)</title>
		<link>http://codesources.net/zagruzka-html-v-iwebbrowser-delphi/</link>
		<comments>http://codesources.net/zagruzka-html-v-iwebbrowser-delphi/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:17:31 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Интернет]]></category>
		<category><![CDATA[Интерфейс]]></category>

		<guid isPermaLink="false">http://codesources.net/?p=60</guid>
		<description><![CDATA[uses ActiveX; procedure WB_LoadHTML(WebBrowser: TWebBrowser; HTMLCode: string); var sl: TStringList; ms: TMemoryStream; begin WebBrowser.Navigate(&#8216;about:blank&#8217;); while WebBrowser.ReadyState &#38;lt; READYSTATE_INTERACTIVE do Application.ProcessMessages; if Assigned(WebBrowser.Document) then begin sl := TStringList.Create;<br /><a class="more-link" href="http://codesources.net/zagruzka-html-v-iwebbrowser-delphi/">keep reading</a>]]></description>
			<content:encoded><![CDATA[<p>uses<br />
ActiveX;</p>
<p>procedure WB_LoadHTML(WebBrowser: TWebBrowser; HTMLCode: string);<br />
var<br />
sl: TStringList;<br />
ms: TMemoryStream;<br />
begin<br />
WebBrowser.Navigate(&#8216;about:blank&#8217;);<br />
while WebBrowser.ReadyState &amp;lt; READYSTATE_INTERACTIVE do<br />
Application.ProcessMessages;</p>
<p>if Assigned(WebBrowser.Document) then<br />
begin<br />
sl := TStringList.Create;<br />
try<br />
ms := TMemoryStream.Create;<br />
try<br />
sl.Text := HTMLCode;<br />
sl.SaveToStream(ms);<br />
ms.Seek(0, 0);<br />
(WebBrowser.Document as<br />
IPersistStreamInit).Load(TStreamAdapter.Create(ms));<br />
finally<br />
ms.Free;<br />
end;<br />
finally<br />
sl.Free;<br />
end;<br />
end;<br />
end;</p>
]]></content:encoded>
			<wfw:commentRss>http://codesources.net/zagruzka-html-v-iwebbrowser-delphi/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Результат HTTP-запроса (Delphi)</title>
		<link>http://codesources.net/rezultat-http-zaprosa-delphi/</link>
		<comments>http://codesources.net/rezultat-http-zaprosa-delphi/#comments</comments>
		<pubDate>Wed, 01 Feb 2012 14:16:47 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Delphi]]></category>
		<category><![CDATA[Интернет]]></category>

		<guid isPermaLink="false">http://codesources.net/?p=58</guid>
		<description><![CDATA[uses IdMultipartFormData; { &#8230;. } procedure TForm1.Button1Click(Sender: TObject); var data: TIdMultiPartFormDataStream; begin data := TIdMultiPartFormDataStream.Create; try { add the used parameters for the<br /><a class="more-link" href="http://codesources.net/rezultat-http-zaprosa-delphi/">keep reading</a>]]></description>
			<content:encoded><![CDATA[<p>uses IdMultipartFormData;</p>
<p>{ &#8230;. }</p>
<p>procedure TForm1.Button1Click(Sender: TObject);<br />
var<br />
data: TIdMultiPartFormDataStream;<br />
begin<br />
data := TIdMultiPartFormDataStream.Create;<br />
try<br />
{ add the used parameters for the script }<br />
data.AddFormField(′param1′, ′value1′);<br />
data.AddFormField(′param2′, ′value2′);<br />
data.AddFormField(′param3′, ′value3′);</p>
<p>{ Call the Post method of TIdHTTP and read the result into TMemo }<br />
Memo1.Lines.Text := IdHTTP1.Post(′http://localhost/script.php′, data);<br />
finally<br />
data.Free;<br />
end;<br />
end;</p>
]]></content:encoded>
			<wfw:commentRss>http://codesources.net/rezultat-http-zaprosa-delphi/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

