Implements an image recognition captcha.

"; break; case 'admin/modules#description': case 'admin/modules/textimage': case 'admin/textimage': $output = t('Implements an image recognition captcha.'); break; } return $output; } function textimage_captchachallenge(&$form) { $form['captcha_response'] = array ( '#type' => 'textfield', '#title' => t('Captcha Validation'), '#default_value' => '', '#required' => TRUE, '#validate' => array('_captcha_validate' => array()), '#description' => t('Please type in the letters/numbers that are shown in the image above.'), '#prefix' => 'Captcha Image: you will need to recognize the text in it.', ); return $form; } function textimage_captchavalidate(&$captcha_word, &$correct) { $captcha_word = drupal_strtolower($captcha_word); if (($_SESSION['captcha'] != '') && $captcha_word == $_SESSION['captcha']) { $correct = true; } else { $correct = false; form_set_error('captcha_response', t('The image verification code you entered is incorrect.')); } } /** * Implementation of hook_menu(). */ function textimage_menu($may_cache) { $items = array(); $suffix = ''; if (arg(2)!=null) $suffix='/'.arg(2); $items[] = array( 'path' => '_textimage/image'.$suffix, 'title' => t('textimage'), 'callback' => '_textimage_image', 'access' => user_access('access textimages'), 'type' => MENU_CALLBACK ); return $items; } function textimage_perm() { return array('access textimages'); } function textimage_settings() { $fonts_path = variable_get("textimage_fonts_path", ""); $images_path = variable_get("textimage_images_path", ""); //check for GD if (!function_exists(imagecreate)) drupal_set_message(t('Image library not available. Textimage needs the GD library extension to be installed. Please install GD.')); //check for TTF support elseif (!function_exists(imagettftext)) drupal_set_message(t('Your image library does not seem to have TrueType font support. Textimage will work, but will use the default inbuilt font.'),'status'); //check for valid font path elseif ($fonts_path!="" && !is_dir($fonts_path)) drupal_set_message(t('The current font path is invalid. The default font will be used.')); //check for valid image path if ($images_path!="" && !is_dir($images_path)) drupal_set_message(t('The current images path is invalid. No images will be used.')); //Fonts settings $form['fonts'] = array( '#type' => 'fieldset', '#title' => t('Fonts settings'), '#collapsible' => TRUE, '#collapsed' => FALSE ); $form['fonts']['textimage_use_only_upper'] = array( '#type' => 'checkbox', '#title' => t('Use only Uppercase'), '#default_value' => variable_get('textimage_use_only_upper',0) ); $form['fonts']['textimage_fonts_path'] = array( '#type' => 'textfield', '#title' => t('TrueType Fonts Path'), '#default_value' => $fonts_path, '#size' => 30, '#maxlength' => 255, '#description' => t('Location of the directory where the Truetype (.ttf) fonts are stored. If you do not provide any fonts, the module will use the default font for text. Relative paths will be resolved relative to the Drupal installation directory.'), ); $form['fonts']['textimage_font_size'] = array( '#type' => 'textfield', '#title' => t('Font Size'), '#default_value' => variable_get('textimage_font_size',24), '#size' => 5, '#maxlength' => 2, '#description' => t('Font size of Captcha text (in pixels).'), '#validate' => array("_textimage_number_validate" => array("textimage_font_size")), ); $form['fonts']['textimage_char_spacing_max'] = array( '#type' => 'textfield', '#title' => t('Character Spacing'), '#default_value' => variable_get('textimage_char_spacing_max',10), '#size' => 5, '#maxlength' => 4, '#description' => t('Sets the kerning between letters in Captcha. Higher numbers indicate more spacing.'), '#validate' => array("_textimage_number_validate" => array("textimage_char_spacing_max")), ); $form['fonts']['textimage_char_jiggle_amount'] = array( '#type' => 'textfield', '#title' => t('Character Jiggle'), '#default_value' => variable_get('textimage_char_jiggle_amount',5), '#size' => 5, '#maxlength' => 2, '#description' => t('Sets the amount of up and down movement in the Captcha letters. Higher numbers indicate more jiggling.'), '#validate' => array("_textimage_number_validate" => array("textimage_char_jiggle_amount")), ); $form['fonts']['textimage_char_rotate_amount'] = array( '#type' => 'textfield', '#title' => t('Character Rotation'), '#default_value' => variable_get('textimage_char_rotate_amount',5), '#size' => 5, '#maxlength' => 2, '#description' => t('Sets the amount of rotation in the Captcha letters (in degrees, only works with non-default fonts).'), '#validate' => array("_textimage_number_validate" => array("textimage_char_rotate_amount")), ); $form['fonts']['textimage_char_size_amount'] = array( '#type' => 'textfield', '#title' => t('Character Size Adjustment'), '#default_value' => variable_get('textimage_char_size_amount',2), '#size' => 5, '#maxlength' => 2, '#description' => t('Sets the amount of variation in size between the different letters in the Captcha (in pixels).'), '#validate' => array("_textimage_number_validate" => array("textimage_char_size_amount")), ); //Image settings $form['images'] = array( '#type' => 'fieldset', '#title' => t('Image settings'), '#collapsible' => TRUE, '#collapsed' => FALSE ); $form['images']['textimage_images_path'] = array( '#type' => 'textfield', '#title' => t('Background Images Path'), '#default_value' => $images_path, '#size' => 30, '#maxlength' => 255, '#description' => t('Location of the directory where the background images are stored. If you do not provide a directory, solid colors will be used. Relative paths will be resolved relative to the Drupal installation directory.'), ); $form['images']['textimage_image_noise'] = array( '#type' => 'textfield', '#title' => t('Image Noise (pixels)'), '#default_value' => variable_get('textimage_image_noise',4), '#size' => 5, '#maxlength' => 4, '#description' => t('Sets the amount of noise (random pixels) in the Captcha image. Higher numbers indicate more noise.'), '#validate' => array("_textimage_number_validate" => array("textimage_image_noise")), ); $form['images']['textimage_image_lines'] = array( '#type' => 'textfield', '#title' => t('Image Noise (lines)'), '#default_value' => variable_get('textimage_image_lines',4), '#size' => 5, '#maxlength' => 4, '#description' => t('Sets the amount of noise (random lines) in the Captcha image. Higher numbers indicate more noise.'), '#validate' => array("_textimage_number_validate" => array("textimage_image_lines")), ); $form['images']['textimage_image_margin'] = array( '#type' => 'textfield', '#title' => t('Image Margin'), '#default_value' => variable_get('textimage_image_margin',10), '#size' => 5, '#maxlength' => 4, '#description' => t('Set a distance between the Captcha letters and the edges of the image.'), '#validate' => array("_textimage_number_validate" => array("textimage_image_margin")), ); $form['info'] = array( '#type' => 'fieldset', '#title' => t('Image and font information'), '#collapsible' => TRUE, '#collapsed' => FALSE ); if (isset($fonts_path)) { $imagefontinfo .= t('Number of fonts found: ').count(_textimage_font_list()); } if (isset($images_path)) { $imagefontinfo .= '
'.t('Number of background images found: ').count(_textimage_image_list()); } $gdinfo = gd_info(); $imagefontinfo .= '
'.t('GD Version: ').$gdinfo["GD Version"]; $imagefontinfo .= '
'.t(' FreeType Support: '); $imagefontinfo .= ($gdinfo["FreeType Support"]==true) ? 'True' : 'False'; $imagefontinfo .= '
'; $form['info']['captcha_info'] = array ( '#type' => 'item', '#value' => $imagefontinfo, ); return $form; } function textimage_settings_form_validate ($form_id,$form) { //check for valid font path if ($form['textimage_fonts_path'] !="" && !is_dir($form['textimage_fonts_path'])) form_set_error('textimage_fonts_path', t('The entered font path is invalid')); //check for valid image path if ($form['textimage_images_path'] !="" && !is_dir($form['textimage_images_path'])) form_set_error('textimage_images_path', t('The entered image path is invalid')); } function _textimage_number_validate ($field,$fieldName) { if (!is_numeric($field['#value'])) { form_set_error($fieldName,t("The value for")." ".t($field['#title'])." ".t("must be a number")); } } /** * Prints an image containing a textimage code. */ function _textimage_image() { //if we don't have GD2 functions, we can't generate the image if (!function_exists('imagecreatetruecolor')) return; // Set headers header('Expires: Mon, 01 Jan 1997 05:00:00 GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: post-check=0, pre-check=0', false); header('Pragma: no-cache'); header('Content-type: image/png'); $string = _textimage_code(); // Get truetype font list $fonts = _textimage_font_list(); // Get the background images list $images = _textimage_image_list(); // Randomization amounts: $charSpacingMax = variable_get('textimage_char_spacing_max',10); // Letter spacing max (pixels) $charSpacingMin = max($charSpacingMax*.5,0); // Letter spacing minimum (pixels) $charJiggleAmount = variable_get('textimage_char_jiggle_amount',5); // Up and down randomization (pixels) $charRotateAmount = variable_get('textimage_char_rotate_amount',5); // Character rotation amount (degrees) $charSizeAmount = variable_get('textimage_char_size_amount',2); // Character size amount (pixels) $imageRotateAmount = variable_get('captcha_image_rotate_amount',12); // Image rotation amount (degrees) // Static amounts: $charInitialSize = variable_get('textimage_font_size',24); // Initial Font $imageNoise = variable_get('textimage_image_noise',4); // Amount of noise added to image $imageLines = variable_get('textimage_image_lines',4); // Amount of noise added to image $imageMargin = variable_get('textimage_image_margin',10); // Margin around image (pixels) // write text using a truetype font if (function_exists(imagettftext) && count($fonts) > 0) { // Initialize variables for the loop $characterDetails = array(); // contains the final info about each character // Build a list of character settings for the captcha string for ($i=0;$i $charSize, "angle" => $charAngle, "x" => $x, "y" => $y, "color" => $foreground, "font" => $font, "char" => $char ); // Increment the image size $imageWidth = $x + $charWidth; $imageHeight = max($imageHeight,$y+$charJiggleAmount); } // Create the image based off the string length and margin if (count($images) > 0) { // We're going to be using an image, and need a tranparent background to start with $im = _textimage_create_transparent_image($imageWidth+2*$imageMargin, $imageHeight+2*$imageMargin); $noisecolor = imagecolorallocatealpha($im, 0, 0, 0, 127); } else { // Just make a plain-jane color brackground $im = imagecreatetruecolor($imageWidth+2*$imageMargin, $imageHeight+2*$imageMargin); $background = imagecolorallocate($im, rand(180, 250), rand(180, 250), rand(180, 250)); $noisecolor = $background; imagefill($im, 0, 0, $background); } // Specify colors to be used in the image $foreground = imagecolorallocate($im, rand(0, 80), rand(0, 80), rand(0, 80)); foreach($characterDetails as $char) { // draw character imagettftext($im,$char['size'],$char['angle'],$char['x']+$imageMargin,$char['y']+$imageMargin,$foreground,$char['font'],$char['char']); } } else { // write text using a built-in font $x = 0; $y = 0; $imageWidth = 60 + drupal_strlen($string)*$charSpacingMax*.35; $imageHeight = 30 + $charJiggleAmount; // Create the image if (count($images) > 0 && function_exists(imagecolorallocatealpha)) { // We're going to be using an image, and need a tranparent background to start with $im = _textimage_create_transparent_image($imageWidth, $imageHeight); $noisecolor = imagecolorallocatealpha($im, 0, 0, 0, 127); } else { // Just make a plain-jane color brackground $im = imagecreatetruecolor($imageWidth, $imageHeight); $background = imagecolorallocate($im, rand(180, 250), rand(180, 250), rand(180, 250)); $noisecolor = $background; imagefill($im, 0, 0, $background); } // Add the text for ($i=0;$i 0) { // Prepare a larger image with a background image $im2 = _textimage_create_transparent_image($imageWidth, $imageHeight); } else { // Prepare a larger image with a solid color $im2 = imagecreatetruecolor($imageWidth, $imageHeight); imagefill($im2, 0, 0, $background); } $result = imagecopyresampled ($im2, $im, $imageMargin, $imageMargin, 0, 0, $imageWidth, $imageHeight, imagesx($im), imagesy($im)); $im = $im2; } // strikethrough imageline($im, rand(0, 120), rand(0, 120), rand(0, 120), rand(0, 120), $foreground); // Add Noise for ($x=0; $x<$imageWidth; $x++) { for ($row=0; $row<$imageNoise;$row++) { $y = rand(0,$imageHeight); imagesetpixel($im, $x, $y, $noisecolor); } } // Add Lines and Ellipses for ($x=0; $x<$imageLines;$x++) { imageline($im, rand(0, $imageWidth), rand(0, $imageHeight), rand(0, $imageWidth), rand(0, $imageHeight), $noisecolor); imageellipse($im, rand(0, $imageWidth), rand(0, $imageHeight), rand(0, $imageWidth), rand(0, $imageHeight), $noisecolor); } // Fill image with a random background image if available if (count($images) > 0) { $image = $images[rand(0,count($images)-1)]; _textimage_apply_background_image($im,$image); } //output to browser imagepng($im); imagedestroy($im); } /** * Returns a random string for use in a captcha */ function _textimage_code() { $consts='bcdgjxvmnprst'; $vowels='aeiou'; for ($x=0; $x < 6; $x++) { mt_srand ((double) microtime() * 1000000); $const[$x] = drupal_substr($consts,mt_rand(0,drupal_strlen($consts)-1),1); $vow[$x] = drupal_substr($vowels,mt_rand(0,drupal_strlen($vowels)-1),1); } $string = $const[0] . $vow[0] .$const[2] . $const[1] . $vow[1] . $const[3] . $vow[3] . $const[4]; $string = drupal_substr($string,0,rand(4,6)); //everytime we create a new code, we write it to session $_SESSION['captcha'] = drupal_strtolower($string); if(variable_get('textimage_use_only_upper',0)) $string = drupal_strtoupper($string); return $string; } /** * Returns an array of files with TTF extensions in the specified directory. */ function _textimage_font_list() { $fontdir = variable_get("textimage_fonts_path", ""); $filelist = array(); if (is_dir($fontdir) && $handle = opendir($fontdir)) { while ($file = readdir($handle)) { if (preg_match("/\.ttf$/i",$file) == 1) $filelist[] = $fontdir.'/'.$file; } closedir($handle); } return $filelist; } /** * Returns an array of files with jpg, png, and gif extensions in the specified directory. */ function _textimage_image_list() { $imagesdir = variable_get("textimage_images_path", ""); $filelist = array(); if (is_dir($imagesdir) && $handle = opendir($imagesdir)) { while ($file = readdir($handle)) { if (preg_match("/\.gif|\.png|\.jpg$/i",$file) == 1) $filelist[] = $imagesdir.'/'.$file; } closedir($handle); } return $filelist; } /** * Overlays an image to the supplied image resource */ function _textimage_apply_background_image (&$imageResource,$imageFile) { $backgroundResource = image_gd_open($imageFile,substr($imageFile,-3)); // Copy the text onto the background $backX = imagesx($backgroundResource); $backY = imagesy($backgroundResource); $textX = imagesx($imageResource); $textY = imagesy($imageResource); $randomBackX = rand(0,$backX-$textX); $randomBackY = rand(0,$backY-$textY); // Place the text onto a random location of the background image imagecopyresampled($backgroundResource,$imageResource,$randomBackX,$randomBackY,0,0,$textX,$textY,$textX,$textY); // Crop the background image to the original image size imagecopyresampled($imageResource,$backgroundResource,0,0,$randomBackX,$randomBackY,$textX,$textY,$textX,$textY); } /** * Creates transparent image resources for images with graphic backgrounds */ function _textimage_create_transparent_image($x, $y) { $i = imagecreatetruecolor($x, $y); $b = imagecreatefromstring(base64_decode(_text_image_blankpng())); imagealphablending($i, false); imagesavealpha($i, true); imagecopyresized($i, $b ,0 ,0 ,0 ,0 ,$x, $y, imagesx($b), imagesy($b)); return $i; } function _text_image_blankpng() { $c = "iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29m"; $c .= "dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADqSURBVHjaYvz//z/DYAYAAcTEMMgBQAANegcCBNCg"; $c .= "dyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAAN"; $c .= "egcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQ"; $c .= "oHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAA"; $c .= "DXoHAgTQoHcgQAANegcCBNCgdyBAgAEAMpcDTTQWJVEAAAAASUVORK5CYII="; return $c; } ?> ОСЭП (Часть 2) | Odesskiy Listok

ОСЭП (Часть 2)

[Продолжение. Начало в №162]

Стабилизация быта
Как это ни покажется странным, утром мы уже проснулись веселыми и довольными. Цвет вокруг был ярким и приятным, а запаха не было. Свежий утренний бриз работал в направлении Судака. Разбудили нас ранние любители купания, которые стайками пробегали мимо, но по противоположному берегу. Все они, по неизвестной причине, задерживались в кустах напротив.
- Гадят, что ли? – сказал задумчиво Чмона. – Необходимо провести рекогносцировку на местности и принять меры.
Мы все еще лежали в палатках, высунув на свежий воздух головы.
Суслов, который уже бодрствовал, глядя задумчиво вдаль, встал и пошел на разведку. Какое ни есть, но дело. А то попросят ленивцы кашку сварить. Не рискуя погружаться в воду ручья, содержание которой было психологически подозрительным, он побрел в обход.
Потом он появился на том берегу и стал докладывать.
- Следов надругательства над природой не замечено, Если и есть следы этого по малой нужде, это не ощущается.
- Ну и фиг с ним, – сказал Чмона. – А что за кустики?
- Шиповник вижу. И красненькие ягодки, может быть, волчьи ягоды.
- А ну, кинь веточку.
Витя обвязал веточкой камень и кинул.
- Балда ты, необразованная, – сказал Олег. – Это уникальная ягода. Кизил называется. Для лучшего усвоения знаний сваришь из нее компот.
Но Витя исчез из поля зрения и возник из кустов на стволе стоящего в глубине зарослей дерева.
- Ребята, да это шелковица, но я такой не видел.
Витя раскрыл ладонь, на которой лежала огромная ягода.
- Кончай разведку. Тащи образцы. Будем распределять обязанности на день, – сказал Олег.
- А я все думал, чего они там, у дерева делают? Вот, все спелое внизу обобрали. Но я сорвал вверху.
Шелковица оказалась вкусной. Огромные, истекающие соком плоды.
- Молодец, – похвалил Олег. – Но я уже сказал, что для усвоения местной флоры ты должен будешь к вечеру сварить компот. А мы теперь имеем под боком персональный питомник витаминов.
- Хорошо, – сказал Суслов, – мне самому интересно. Но утром все обнесут прохожие.
-Ну и жлоб ты, – сказал Олег, – сад-огород общественный. Кто раньше встал, тот больше съел. Туристский закон. Но ты всегда можешь потренироваться лазить наверх. По инстинктивным позывам наших хвостатых предков.
Перекусив всухомятку, чем бог послал, ребята ушли в город Судак, а я сел писать этюд.

Когда дела в городе были улажены и ребята возвратились, было решено устроить предвечернее купание. Было жарко, и народа вокруг по сему случаю поубавилось. Видно, прогулки были в моде в утреннее время, а теперь все купались поближе к местам постоянного обитания. Вот мы и пошли всей кампанией, даже Витя. Деньги и ценные вещи взяли с собой.
И вот мы в камнях. Сидим, охлаждаемся перед водными процедурами. И вдруг…
Сидевшая на камне Майя прощебетала:
- Розочки!
И с округлившимися глазами рухнула в воду. Хорошо, когда человек вырос на Днепре. Да еще и в Хортице, где камни – дело привычное. Майя, которая плавала, как рыба и была хорошо скоординирована, успела сгруппироваться и ушла под воду нормально. Мы с Олегом быстро прореагировали и были в воде буквально через мгновенье. И вовремя. Вынырнувшая Майя, повторив свое: «розочки…» и слегка хлебнув воды, поперхнулась и ушла под воду. Пришлось спасать подругу.
И вот мы выползли на камни. Давясь смехом и водой она, как заведенная, поминала пресловутые розочки и хохотала.
- Ты что, подруга, перегрелась, – проворчал Олег, – «гальюнцинации»?
- Розочки, – сказала Майя, проглотив остатки воды.
При этом палец ее уперся в колено стоящего в театральной позе (руки на груди, правая нога вперед) Суслова.
Теперь настала очередь падать с камня Чмоне. Дико хохотнув, он булькнул в воду, но осторожнее.
- Ну ты даешь! – сказал он, выползая обратно. – Ты что, нормальных татуировок не видела? При этом тебе бы следовало учесть трудное детство и заблуждения юности Вити, а не устраивать спектакль.
А теперь поясним. Витя Суслов таки являлся объектом, достойным изучения. Рассматривать его галерею было и интересно, и поучительно. Майя увидела розочки и навившихся на их черенки змей. А остальное! На груди – ангелочки, несущие крест на цепочке, обязательные портреты вождей под сердцем. А на спине… Устать можно от перечисления. Мы-то с Олегом насмотрелись. Когда Витя узнал, что мы едем в Киев, то, стесняясь и смущаясь, сказал:
- Говорят, что в Киеве есть салон, где выводят это безобразие.
Салон был, при Институте красоты. А заведовала им мама друзей – соперников моего спортивного брата. Мы с Олегом получили, по наводке, подробную консультацию и списали все расценки. Приехав, мы все обмерили и добросовестно просчитали затраты на услуги косметологов. Не считая мук физических на саму операцию. Добрейшая душа, Витя сказал:
- Я готов потерпеть боль физическую. Но я грубо прикинул (что и мы с Чмоной уже сделали, но не хотели огорчать друга), что работать на это при моей зарплате, расходуя ее целиком, – до пенсии не уложусь.
После просветительной беседы Майечка успокоилась и жалела Витю.
- Да, красиво и занимательно, – говорила она, – ты уж, Витечка, на меня не обижайся, я все это изучать буду.
Знала бы она, что самое интересное этому самому изучению было недоступно. Интимное тату, как теперь говорят. Не для дам. Только для дамы сердца.

Город Судак
Город Судак оказался живописным, но диким. Уже при появлении нашей компании местное население зароптало. Это теперь цивилизация взяла свое. А в те далекие времена чистота нравов царила на всем его пространстве. Местная милиция, дружинники и простые жители дружно и с негодованием осудили живописные самопальные шорты Чмоны, брюки Майки и солнцезащитные очки всех. А татуировки на руках прилично одетого Суслова?! В магазинах тетки прижимали к груди кошелки, а мужчины страховали карманы.
- Вы, ребята, не дразните местное население, – сказал начальник милиции, в отделение которой нас препроводил милиционер, шлявшийся у магазина. – Я понимаю, что вы люди современные, но и на пожелания местных жителей я реагировать обязан.
Мы так легко согласились на визави с властью потому, что возникли коллизии на почте. Мы дали телеграммы в Днепр родителям Суслова и Майки о том, чтобы они разыскали и ценным срочным отправлением переслали в Судак паспорта. Однако Олег смекнул: а как потом эти ценные письма получить, если пересылка придет до востребования, а ее без документа, который в этой же пересылке упакован, не выдадут на руки? Вот и пришли посоветоваться с главным милиционером. Начальник милиции слегка развеселился. Связался по телефону с майором Петровым, с ним переговорил. В итоге Чмона побежал на заставу и получил свой паспорт. А Витя и Майка сели карябать на бумаге доверенности на его имя. Когда он появился, в доверенность вписали номер его паспорта, и начальник милиции доверенности заверил.
- Петров сказал, что вы хорошие ребята, хотя и малость бестолковые. Желаю вам хорошего отдыха. Если нужно будет, заходите.
Пришлось подчиниться местным нравам, и в город выходила Майя в приличном платье, проверяя почту и прикупая пищу.

Мы уже на нашем месте осели и освоились. Вечером с удовольствием попили приготовленный Витей компот из местных фруктов.
- Не мешало бы и кофейку на ночь, – сказал я задумчиво.
Олег взял в руки канистру и подозрительно посмотрел на Суслова.
- Витя, а почему канистра сухая. Кстати, она ведь была сухой и в тот момент, когда мы ушли купаться, а ты варить компот.
Витя замялся, потихоньку стал отруливать на край полянки и задумчиво смотреть на звезды.
- Эй, пионер, что ты там, в дополнение к изучению флоры, астрономией увлекся? Подозрительное поведение. Чувствую, что тебя, как Паниковского из настольной книги, придется побить. Может быть, даже ногами. Экспериментатор вшивый. Ты ведь сварил все на воде из нашего родного ручья!
- Ну, не совсем. Было немного нормальной воды в канистре. Но ты смотри. Прошло уже два часа, а все здоровы. Особенно ты. Есть силы грозить побоями труженику.
- Ну и фиг с ним, – баба з возу. В наказание будешь варить кофе. Но хоть сходи и зачерпни там, поближе к морю.
Так пал последний барьер: все, что кипятилось, бралось из ручья. А вместо питьевой воды использовалось парное и отстоявшееся молочко. Изредка ситро «Дюшес» и минеральная вода, которую приносили из города.

Ночные купания
Эмоциональная прелесть ночных купаний недоступна тем, кто не имел счастья этим побаловаться. Правда, купание в Крыму в это время ночью было делом рискованным. Бдительные пограничные прожектора непрерывно шарили по всему подконтрольному берегу, а неутомимые патрули частенько выхватывали из воды нарушителей и требовали документы. Правда, драконовских мер не было, дело ограничивалось поучительными беседами. Однако вся пикантность ситуации заключалась в нюансах. Дело в том, что всякое ночное купание теряло смысл, когда на тебе был напялен, пусть и легкий, но стесняющий свободу купальный костюм. По принципу – девочки налево, мальчики направо – все раздевались до состояния первобытного. И некоторое время ныряли и плескались на некотором расстоянии друг от друга, а потом быстро сбивались в общую стаю. При появлении пограничников, мужчины шли напролом, а женщины, сбившись стайкой, устраивали базар. И стояли так, под лучами мощных фонариков, как нереиды, правда, изрядно продрогшие, пока мужчины не приносили им чего- нибудь для прикрытия наготы.
Правда, был в нашей истории случай, когда энергичная Марина вышла из воды и королевской походкой пошла в упор на молоденького лейтенантика, командира наряда.
- За мной девочки, – сказала она, – ребята молодые, настоящего женского тела на свету не видели.
Лейтенант смутился и в наступившей после яркого света темноте вежливо извинился.
- Можете купаться, – сказал он, после проверки паспортов у тех, кто их имел с собой, – но у берега, далеко не заплывайте.
- Спасибо, – сказала Марина, выжимая волосы, – мы это сделаем завтра. С вашего разрешения.
Вот и мы стали выбираться в наши камни под Алчаком ночью.
А море в этот наш приезд светилось. Бывает свечение моря, приходилось это и потом наблюдать, но свечение моря в тот наш приезд было исключительным. По крайней мере, я такого больше не видел. Каждый удар по воде рукой вызывал феерическое свечение, а нырнувший с камня человек уходил в сверкающих брызгах и светился под водой, как привидение.
Естественно, нам такое купание понравилось. Если первый наш выход мы обставили конспиративными мерами и тщательно скрывались от прожекторов, а уж для обнаружения патруля так прямо свое секретное наблюдение выставляли, то потом совсем обнаглели. Прошел наш родной патруль, поздоровался, тщательно пересчитал нас по головам и отбыл. Мы еще некоторое время выжидаем, а потом перебежками, перебежками – и в наши камни. Купаемся и наблюдаем по часам за временем появления патруля с обратным визитом – и тихенько к себе, в лагерь. Не то, чтобы мы были очень напуганы или боялись санкций. В майора Петрова мы верили. Но огорчать его вызывающим поведением не хотели.
Но время шло, ночи становились очень теплыми, а вода прогревалась, и свечение оставалось на уровне. Вот и стали мы шастать туда с перерывами по несколько раз за ночь. Да и шумно шастать. А патруля все нет и нет. Может быть, по другому маршруту ходить начали. Вечерком пройдут, сосчитают и пропали. Но через пару ночей, когда мы уже пятый раз в камни выбрались и большой базар с нырянием устроили, вдруг отделился от камня силуэт пограничника.
- Ребята, – сказал он, – майор Петров вас, конечно, любит и доверяет. Но нельзя же злоупотреблять. Мы понимаем, что он этот секрет организовал для наших тренировок. Но раньше хоть вздремнуть на посту удавалось. Покупались бы и – на боковую. А то я вот шастаю с вами туда и сюда. Кстати, сигареткой не угостите?
- Молодец, служивый, – сказал Чмона. – Вот тебе пачка. Я все равно не курю. А Маслак пусть здоровье побережет. А тебя мы за искренность подстрахуем. Твои ребята появятся, растолкаем. Мы их по часам отслеживаем. А пожевать не хочешь?
- Не откажусь.
- Ну вот и молодец. А пошли с нами в лагерь, чайком угостим.
- Нет. Я здесь останусь. Сон развеяло.
Так и ходили мы вместе с пограничниками. Мы – купаться, они – секретному искусству маскировки научаться. Но с этого дня, вроде, роднее стали. Считают. Улыбаются. Иногда сигаретки стреляют или чего-нибудь пожевать вкусненького. И все друг другом премного довольны.

Конец ссылки
День, когда Майя пришла из города и доложила, что письма прибыли, причем сразу два, не вызвал великого энтузиазма. Мы уже привыкли и обленились в своем закутке. Майя принесла по этому поводу бутылочку «Кокура», нового судакского вина, как нам сказали в главном гастрономе. Откупавшись последнюю ночь, мы попрощались с нашими пограничниками, а поздним утром свернули лагерь и двинулись в путь.
В маленьком городке слухи растекаются. Нас, несмотря на темные очки и странный вид, уже встречали, как своих, доброжелательно.
Получив паспорта на почте и прикупив продуктов, мы явились на заставу.
- Ну, как жилось,– спросил майор Петров, – куда дальше двинетесь?
-В Новый Свет. А жилось хорошо. Даже привыкли.
- Ну и ладно, – сказал Петров, выдавая нам документы. – Заходите в гости.
- Обязательно.

В этот раз мы пошли в обход крепости снизу. Руины и здесь были живописными. Остатки стен и ворота. И вдруг Витя пропал.
- Наверно заблудился в трех домах, – сказал Олег.
Мы стали обследовать пространство у домиков, которые живописно кучковались возле руин. Точно, стоит Суслов и пялится вверх на огромные деревья за забором.
- Чего потерял, почему стоим? – сказал я, первым его обнаружив.
- Смотри, Маслак, – сказал Витя, – шелковица. И еще крупнее нашей из резервации. Величиной с ладонь. И спелая.
- Но, в отличие от той, из резервации – это частный двор, и шелковица не имеет общественного статуса.
В это время подошли остальные, а из калитки вышла молодая женщина с ведром.
- Что, нравится? – спросила она, – заходите и кушайте на здоровье.
- А можно? – спросила Майка.
- Да она и так пропадает, только двор весь запачкала. Уже и куры не клюют. Не на базар же ее таскать за копейки. Все соседи уже и наелись, и джемов наварили.
- И сколько же лет такому дереву? – сказал философски Олег.
- Может быть сто, а может быть и меньше. Уже при моих родителях деревья стояли старыми, – сказала женщина.
И начался пир. Мужчины, как обезьяны, – вверху, а Майя собирает все в котелок. Наелись до отвала. А когда устали, глянули друг на друга и ошалели. Все соком забрызганы. Когда срываешь, сок так и течет. Потом ножом и ножницами резали. А Майке внизу больше всех досталось. Она от добросовестности в светлое платье вырядилась.
Подошли к ведру с водой и стали делать попытки смыть пятна. Я из своей детской деревенской жизни урок усвоил. Пятна сока слюной отмываются, высосал и простирал. Действует. Но где такое количество слюны набрать?
А здесь и женщина заходит с новым ведром воды. Увидела нас и хохочет.
- Ну вот, – говорит Олег, – у людей горе…
- Пустяки, – говорит она, – найдите зеленые шелковицы и их соком запачканные места натрите. Потом все водой смоется.
Мы не поверили, но выхода нет. Нашли зелень и натерли. И точно - все смылось.
- Народное средство, – смеется хозяйка. – А мы с соседкой сидели на лавочке и за вами наблюдали. Приятно, когда люди так радуются. На здоровье вам.

Лирическое отступление
На этом мое общение с хозяйкой и шелковицей не закончилось. Идем мы через несколько лет с друзьями. С нами – моя тогда невеста Ия. Проходим мимо дома.
- Смотри, Иечка, какая уникальная шелковица, – говорю.
А Ия шелковицу очень любила. Вдруг слышу от лавочки у забора голос.
-Ну что, длинноносик, – заходи с друзьями шелковицу кушать. Я тебя помню.
Снова поели, нарвали, естественно измазались сами и женщин внизу соком окатили с ног до головы. Но я, на правах знатока, всех соком зеленых шелковиц отмываться научил.
- А кто это такая светленькая красивая девушка? – хозяйка меня спрашивает.
- Невеста моя, – говорю.
- Ну, молодец, – говорит, – А удержишь? Уж больно красивая. Но к тебе приветлива. Желаю счастья.
…Прошли годы, мы с Ией в Крыму были. И в Судак забрели. Проходим мимо. Ия со знакомыми задержалась, а я на углу стою.
- А что, шелковицу кушать не будешь? Думаешь, усы завел, я и не узнала. А где твоя красавица? Небось, сбежала.
- Да нет, сейчас подойдет. О шелковице вспоминала.
И снова шелковицы мы поели и нарвали. Потом друзья удивлялись. А которые по крепости шатались и им перепали только остатки из наших рук, завидовали.
…И еще годы прошли. Я – уже с бородой. Мы с Ией последний раз вместе в Крыму были. И в Судак попал. На скамеечке постаревшая хозяйка сидит.
- Ну, вот, уже бородой замаскировался, длинноносик. А я все думаю, давненько тебя не видела.
С Ией, как со старой знакомой, о чем-то поговорила.
- Хвалила тебя твоя красавица. И то, столько лет вы вместе. Я за вас рада.
…И вот снова попал я в Судак, в год, когда Ию потерял. Мы о ней кино снимали. Все на ходу, времени мало. Но потом автобусом своим с экскурсией для группы в Судак вырвались. Я к дому подхожу, а у забора сидит старушка.
Стал я и на шелковицу во дворе смотрю. Вспоминаю.
- А где же твоя красавица? – говорит старушка. – Запомнила я тебя, хоть ты уже и не юноша.
- Потерял я ее, – говорю. – Месяц назад похоронил. Теперь – сирота.
- Извини, – говорит. – Врезались вы в мою память. И я вот, болею. А ей – Царствие Небесное… Вон, вижу, тебя уже кличут. Заходи в гости, если жива буду.
…И я вот потом, через пару лет, ехал мимо ее дома. А ее на лавочке нет. И спросить постеснялся. Может, жива?

[Продолжение следует]

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

Содержимое этого поля хранится скрыто и не будет показываться публично.
  • Допустимые HTML-теги: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <p> <br> <tr> <td> <table>
  • Строки и параграфы переносятся автоматически.
  • Images can be added to this post.
Больше информации о возможностях форматирования