RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Перевод выполнен Алексеем Паутовым в рамках некоммерческого проекта RussianLDP (http://www.rldp.ru/). Именно на этом сайте и надлежит искать новые версии, если таковые будут.

41. Добавляем функцию local scan в Exim

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

Расширение для проверки содержимого сообщений (глава 40) имеет средство для передачи возможности сканирования на спам и вирусы внешним программам. Вы можете также проверять определенное количество непосредственно в Exim, через строки расширения и условия condition в ACL, который запускается после команды SMTP DATA или ACL для не-SMTP сообщений (см. главу 39), но здесь есть ограничения.

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

Функция local scan запускается единожды для каждого входящего сообщения, в тот момент, когда Exim собирается принять сообщение. Поэтому эта возможность может использоваться для локальных не-SMTP сообщений также, как и для сообщений, пришедших по SMTP.

Exim применяет перерыв к запросам функции local scan, опция которая задает это, называется local_scan_timeout. По умолчанию параметр равен 5 минутам. Ноль обозначает "непрерывно". Exim также настраивает описатели сигналов для SIGSEGV, SIGILL, SIGFPE и SIGBUS перед вызовом функции local scan для того, чтобы общие типы некорректного завершения были очевидны. Если время перерыва превышено или получен один из сигналов, входящее сообщение отклоняется с ошибкой по времени, если это SMTP-сообщение. Для не-SMTP сообщений сообщение сбрасывается, а Exim завершает работу с кодом ошибки. Происшествие отражается в протоколах main и reject.

41.1. Сборка Exim с использованием функции local scan

Для использования функции local scan Вы должны указать местоположение Вашей функции перед сборкой Exim установкой LOCAL_SCAN_SOURCE в файле Local/Makefile. Рекомендованное место хранения: каталог Local, поэтому можно установить:
LOCAL_SCAN_SOURCE=Local/local_scan.c
для примера. Функция должна называться local_scan(). Она вызывается Exim после получения сообщения, перед отправкой кода успешного выполнения. Это после всех запущенных ACL. Код возврата Вашей функции контролирует, принято сообщение или нет. Есть закомментированный шаблон функции (который принимает сообщение) в файле src/local_scan.c_.

Если Вы хотите использовать файл конфигурации Exim для установки опций для функции local_scan(), Вы должны установить
LOCAL_SCAN_HAS_OPTIONS=yes
в файле Local/Makefile (смотрите раздел 41.3 ниже).

41.2. API для local_scan()

Вы должны включить эту строку ближе к началу кода:
#include "local_scan.h"

Этот заголовочный файл определяет количество переменных, других значений и прототип самой функции. Exim написан для использования практически исключительно беззнаковых символов (unsigned char), и одна из вещей, которую определяет заголовок, сокращение для беззнаковых символов, называемое uschar. Он также содержит следующие макроопределения для упрощения отбора символьных строк и указателей на символьные строки.
#define CS  (char *)
#define CCS (const char *)
#define CSS (char **)
#define US  (unsigned char *)
#define CUS (const unsigned char *)
#define USS (unsigned char **)

Прототип функции local_scan():
extern int local_scan(int fd, uschar **return_text);

Аргументы здесь следующие:

  • fd: файловый дескриптор для файла, который содержит тело сообщения (-D файл). Файл открыт для записи и чтения, но обновление не рекомендуется. Предупреждение: вы не должны закрывать файловый дескриптор. Дескриптор позиционируется на 19 символе в файле, в котором первый символ само тело, потому как первые 19 символов ID сообщения, следующий за ним -D и символ newline. Если Вы переместитесь в начало файла, Вы должны использовать макрос SPOOL_DATA_START_OFFSET для того, чтобы сбросить начало данных.
  • return_text: адрес, который Вы можете использовать, возвратив указатель на текстовую строку в конце функции. Значение, на которое он указывает при входе, NULL. Функция должна возвратить значение int, которое является одним из следующих макросов:
  • LOCAL_SCAN_ACCEPT: Сообщение принято. Если Вы пропустите назад строку текста, это сохранится с сообщением и сделает доступным переменную $local_scan_data. Никаких символов новых строк не поддерживается (если таковые присутствуют, они будут превращены в пробелы) и максимальная длина текста 1000 символов.
  • LOCAL_SCAN_ACCEPT_FREEZE: Ведет себя так же, как и LOCAL_SCAN_ACCEPT, за исключением того, что принятое сообщение становится в очередь без незамедлительной доставки и замораживается.
  • LOCAL_SCAN_ACCEPT_QUEUE: Ведет себя так же, как и LOCAL_SCAN_ACCEPT, за исключением того, что принятое сообщение становится в очередь без незамедлительной доставки.
  • LOCAL_SCAN_REJECT: Сообщение отклонено, возвращаемый текст используется как ошибка, которая отсылается обратно отправителю и протоколируется. Символы новой строки поддерживаются, они разрешают многострочный ответ для SMTP-отклонений, но конвертируются в \n в протоколах. Если никакое сообщение не присвоено, используется Administrative prohibition.
  • LOCAL_SCAN_TEMPREJECT: Сообщение временно отклонено. Возвращаемый текст используется как сообщение об ошибке, как и в LOCAL_SCAN_REJECT. Если никакое сообщение не присвоено используется Temporary local problem.
  • LOCAL_SCAN_REJECT_NOLOGHDR: Ведет себя также, как и LOCAL_SCAN_REJECT, за исключением того, что заголовок отклоненного сообщения не записывается в протокол отклонений. Эффект неустановленного rejected_header в выборе протоколов только для этого отклонения. Если rejected_header уже не установлен (см. обсуждение log_selection в главе 48.15), код тот же самый, что и у LOCAL_SCAN_REJECT.
  • LOCAL_SCAN_TEMPREJECT_NOLOGHDR: Этот код такое же изменение LOCAL_SCAN_TEMPREJECT, как и LOCAL_SCAN_REJECT_NOLOGHDR изменение LOCAL_SCAN_REJECT.

Если сообщение не было получено интерактивно по SMTP, об отклонениях сообщается записью в stderr или отправкой письма, как сконфигурировано опциями -oe в командной строке.

41.3. Опции конфигурации для local_scan()

Возможно использовать установку опции, которая устанавливает значения в статических переменных в модуле local_scan(). Если Вы этого хотите, должны иметь строку
LOCAL_SCAN_HAS_OPTIONS=yes
в файле Local/Makefile, когда собираете Exim. Эта строка находится в OS/Makefile-Default, закомментированная. Затем в исходном файле local_scan() Вы должны определить статические переменные для хранения значений и таблицу, определяющую их.

Таблица должна быть вектором, называемым local_scan_options, типа optionlist. Каждая точка входа тройное значение состоящее из имени, типа опции и указателя на переменную, содержащую значение. Точки входа должны следовать в алфавитном порядке. Следуя за local_scan_options, Вы должны также определить значение переменной, называемой local_scan_options_count, содержащую количество входов в таблицу. Вот краткий пример, демонстрирующий две опции:
static int my_integer_option = 42;
static uschar *my_string_option = US"a default string";
optionlist local_scan_options[] =
{
   {"my_integer", opt_int, &my_integer_option},
   {"my_string", opt_stringptr, &my_string_option}
};
int local_scan_options_count =
    sizeof(local_scan_options)/sizeof(optionlist);

Значения переменных могут быть изменены Exim из файла конфигурации включением секции local scan так, как в этом примере:
begin local_scan
my_integer = 99
my_string = some string of text...

Доступные типы данных следующие:

  • opt_bool: Определяет булевую опцию (да/нет). Адрес должен указывать на переменную типа BOOL, которая устанавливается в TRUE или FALSE (являются макросами, определенными как 1 и 0, соответственно). Если Вы хотите оперделить, была ли установлена такая переменная вообще, можете вызвать ее как TRUE_UNSET. Переменные BOOL целочисленные, которые могут содержать более двух значений.
  • opt_fixed: Эта опция определяет число с фиксированной точкой (целочисленное). Адрес должен указывать на переменную типа int. Хранимое значение умножается на 1000, так, например, значение 1.4142 отрежется и сохранится как 1414.
  • opt_int: Эта опция определяет целое число, адрес должен указывать на переменную типа int. Значение может быть определено в любом целочисленном формате, понимаемом Exim.
  • opt_mkint: Тоже самое, что opt_int, за исключением того, что значение выводится в листинг -bP, если в нем точное число килобайт и мегабайт, печатается с суффиксом K или M.
  • opt_octint: Опция тоже определяет число, как целочисленное, только значение интерпретируется всегда как восьмиричное целочисленное. Начинается с цифры 0 и выводится в восьмиричном счислении.
  • opt_stringptr: Определяет значение строки, адрес должен быть указателем на переменную, которая указывает на строку (например, тип uschar *).
  • opt_time: Определяет интервал времени. Адрес должен указывать на переменную типа int. Значение которое туда записывается, число в секундах.

Если в командной строке за local_scan следует параметр -bP, Exim выводит значения всех опций local_scan().

41.4. Доступные переменные Exim

Заголовок local_scan.h дает доступ к некоторым С-переменным. Тут перечислены только те, которые, как гарантируют будут поддерживаться от релиза к релизу. Заметьте, как бы то ни было, Вы можете получить любое значение переменной Exim, вызывая функцию expand_string(). Экспортируемые переменные следующие:

  • int body_linecount: Количество строк в сообщении.
  • int body_zerocount: Число нулевых байт в сообщении.
  • unsigned int debug_selector: Это переменная устанавливается в ноль, когда отладка не производится. Иначе, это набор значений отладочных селекторов. Два бита используются в функции local_scan(), они определяются как макросы:
    • D_v: бит установлен, когда -v присутствует в командной строке. Эта тестовая опция ни на что не влияет, любой вызов может установить ее. Остальные биты могут установить только администраторы.
    • D_local_scan: бит для использования функцией local_scan(). Устанавливается в +local_scan отладочным селектором. По умолчанию не включается в набор отладочных битов. Таким образом, чтобы получить отладочный вывод, только когда +local_scan включен, Вам нужно написать следующее:
      if ((debug_selector & D_local_scan) != 0)
         debug_printf("xxx", ...);
      
  • uschar *expand_string_message: После неудачной попытки вызвать expand_string() (возвращаемое значение NULL) переменная expand_string_message содержит сообщение об ошибке, завершается нулем.
  • header_line *header_list: Указатель на цепочку строк заголовка. Структура header_line обсуждается ниже.
  • header_line *header_last: Указатель на последнюю строку заголовка.
  • uschar *headers_charset: Значение опции конфигурации headers_charset.
  • BOOL host_checking: Эта переменная TRUE в момент проверки хоста, инициализируемого опцией -bh командной строки.
  • uschar *interface_address: IP-адрес интерфейса, который получает сообщения, тип: строка. Значение NULL для локальных сообщений.
  • int interface_port: Порт, на котором было получено это сообщение.
  • uschar *message_id: Переменная, содержащая идентификаторы сообщений Exim для входящих сообщений (значение $message_exim_id), заканчивающаяся нулем.
  • uschar *received_protocol: Имя протокола, по которому было получено сообщение.
  • int recipients_count: Число подтвержденных получателей.
  • recipient_item *recipients_list: Список подтвержденных получателей, хранящийся как вектор длины recipients_count. Структура recipient_item обсуждается ниже. Вы можете добавлять получателей вызывая, receive_add_recipient() (см. ниже). Вы можете удалять получателей, убирая их из вектора и исправляя значение в recipients_count. В частности, устанавливая recipients_count в ноль, удаляете всех получателей. Если затем возвратите значение LOCAL_SCAN_ACCEPT, сообщение будет принято, но тут же исчезнет. Для замещения получателей можете установить recipients_count в ноль и затем вызвать receive_add_recipient() так часто, как это необходимо.
  • uschar *sender_address: Адрес отправителя. Для отвергнутых сообщений это пустая строка.
  • uschar *sender_host_address: IP-адрес хоста отправителя. Для локальных сообщений NULL.
  • uschar *sender_host_authenticated: Имя аутентификационного механизма, который был использован, или NULL, если сообщение было получено не через SMTP-соединение с аутентификацией.
  • uschar *sender_host_name: Имя хоста отправителя, если известно.
  • int sender_host_port: Порт хоста отправителя.
  • BOOL smtp_input: Переменная равна TRUE для всех входящих SMTP, включая BSMTP.
  • BOOL smtp_batched_input: Переменная равна TRUE для входящих BSMTP.
  • int store_pool: Содержимое этой переменной определяет, какой пул памяти будет использоваться для новых запросов. См. секцию 41.8 для более детальной информации.

41.5. Структура header_line

Структура header_line содержит элементы, упомянутые ниже. Вы можете добавить дополнительные строки заголовка, вызывая функцию header_add() (см. ниже). Вы можете комментировать (удалять) линии заголовка, устанавливая их тип в *.

  • struct header_line *next: Указатель на следующую строку заголовка или на NULL для последней строки.
  • int type: Код, идентифицирующий определенные заголовки, которые Exim распознает. Коды, печатные символы, документированные в главе 52 этого руководства. Обратите внимание, любая строка заголовка, тип которой *, не передается с сообщением. Эта отметка используется для строк заголовка, которые были перезаписаны (например, Envelope-sender: header lines). Зачастую, * означает "удалено".
  • int slen: Число символов в строке заголовка, включая символы завершения и символы новой строки.
  • uschar *text: Указатель на текст заголовка. Всегда заканчивается символом новой строки, сопровождаемый нулевым байтом. Внутренние символы новой строки сохраняются.

41.6. Структура recipient_item

Структура recipient_item содержит следующие элементы:

  • uschar *address: Указатель на адрес получателя, который был получен.
  • int pno: Используется Exim позже в обработке, когда главные адреса созданы опцией one_time. Несущественна в то время, когда local_scan() работает и должен содержать всегда -1 на этом этапе.
  • uschar *errors_to: Если значение не NULL, отталкивет сообщение из-за невозможности доставки получателю по адресу, который содержит. Другими словами, отвергает отправителя конверта для одного адресата (сравните с errors_to в общих опциях маршрутизации). Если функция local_scan() устанавливает поле errors_to неквалифицированному адресу, Exim его квалифицирует, используя домен из qualify_recipient. Когда функция local_scan() вызвана, поле errors_to содержит NULL для всех адресатов.

41.7. Доступные функции Exim

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

pid_t child_open(uschar **argv, uschar **envp, int newumask, int *infdptr, int *outfdptr, BOOL make_leader)
Эта функция создает дочерний процесс, который запускает команду, определенную в argv. Окружение этого процесса определено в envp, который может быть NULL, если не передаются переменные окружения. Новое unmask служит для процесса в newumask.

Каналы стандартного ввода и вывода нового процесса уже настроены и возвращаются вызвавшему через аргументы infdptr и outfdptr. Стандартная ошибка приравнивается к стандартному выводу. Если есть дескрипторы для файла "в пути" в новом процессе, то они закрываются. Если последний аргумент TRUE, новый процесс возглавляет группу процессов. Функция возвращает pid нового процесса или -1, если что-то пошло не так.

int child_close(pid_t pid, int timeout)
Функция ждет, когда дочерний процесс завершится или таймаут (в секундах). Значение таймаута установленное в 0, означает ждать столько, сколько потребуется. Возвращаемые значения следующие:

  • >= 0: Завершение процесса корректно, возвращаемое значение указывает статус процесса.
  • < 0 и > -256: Процесс завершен сигналом, возвращаемое значение задает сигнал процесса со знаком минус.
  • -256: Время процесса истекло.
  • -257: Произошла какая-то другая ошибка в wait(), errno все еще установлен.

pid_t child_open_exim(int *fd)
Функция обеспечивает Вам, с помощью нового сообщения, доступ к Exim. Конечно, Вы можете всегда вызвать /usr/sbin/sendmail сами, если хотите, в этом пакете все для Вас. Функция создает канал и подпроцесс, который запускается таким образом:
exim -t -oem -oi -f <>
и возвращает (через аргумент int *) файловый дескриптор для канала, который подключен к стандартному вводу. Конечный результат функции: PID подпроцесса. Затем Вы можете написать сообщение файловому дескриптору, с получателями в поле To, Cc: и/или Bcc: строками в заголовке.

Когда Вы закончите, вызовите child_close(), подождите, пока процесс завершится и получите его статус окончания. Таймаут со значением ноль обычно неплох в этих обстоятельствах. До тех пор, пока Вы не сделаете ошибку в адресе получателя, Вы должны получать код возврата 0.

pid_t child_open_exim2(int *fd, uschar *sender, uschar *sender_authentication)
Эта функция является более сложной версией child_open(). Команда, которая загружает ее:
exim -t -oem -oi -f sender -oMas sender_authentication
Третий аргумент может быть NULL, в этом случае опция -oMas опущена.

void debug_printf(char *, ...)
Это отладочная функция Exim с аргументами, как для printf(). Вывод производится в поток стандартных ошибок. Если отладка не выбрана, вызов debug_printf() не будет иметь эффекта. Обычно Вы должны делать вызовы по состоянию селекторов local_scan, написав это так:
if ((debug_selector & D_local_scan) != 0)
   debug_printf("xxx", ...);

uschar *expand_string(uschar *string)
Интерфейс для расширения строки Exim. Возвращаемое значение: расширяемая строка или NULL, если расширения не произошло. Переменная C expand_string_message содержит сообщение об ошибке после невозможности расширения. Если расширение не меняет строку, возвращаемое значение является указателем на строку ввода. В другом случае, возвращаемое значение указывает на новый блок памяти, который был получен вызовом store_get() (см. раздел 41.8 ниже, где обсуждается выделение памяти).

void header_add(int type, char *format, ...)
Эта функция позволяет добавить дополнительную строку заголовка в конец уже существующей. Первый аргумент: тип, который обычно начинается пробелом. Второй аргумент: форматированная строка, и любое число заменяемых аргументов, как для sprintf(). Вы можете включать внутренний символ новой строки и должны убедится, что строка заканчивается символом новой строки.

void header_add_at_position(BOOL after, uschar *name, BOOL topnot, int type, char *format, ...)
Функция добавляет новую строку заголовка в определенную точку в цепочке заголовков. Сам заголовок определен как для header_add().

Если name NULL, новый заголовок добавляется в конец цепочки, при условии, что after TRUE, или в начало, если after FALSE. Если name не NULL, строки заголовка ищутся до первого неудаленного заголовка, который совпадает с именем. Если что-то найдено, новый заголовок добавляется до него, если after FALSE.

Если after TRUE, добавляется новый заголовок после найденного заголовка и любых найденных последующих с таким же именем (даже если они отмечены как deleted). Если нет совпадений с non-deleted заголовком, то опция topnot проверяет, где был добавлен заголовок. Если он добавлялся, дополнение наверху, если нет, внизу. Таким образом, чтобы добавить заголовок после всех заголовков с полем Received: или в начало, если нет заголовков Received:, Вы должны использовать:
header_add_at_position(TRUE, US"Received", TRUE,
                       ' ', "X-xxx: ...");

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

void header_remove(int occurrence, uschar *name)
Функция удаляет строки заголовка. Если occurrence равно нулю или отрицательное, заголовок удаляется. Если occurrence больше нуля, удаляется часть заголовка. Если никаких совпадений не найдено, функция не делает ничего.

BOOL header_testname(header_line *hdr, uschar *name, int length, BOOL notdel)
Функция проверяет, имеет ли этот заголовок данное имя. Это не просто сравнение строк, потому что непоказываемый пробел допускается между именем и двоеточием. Если аргумент notdel TRUE, тогда возвращаемое FALSE применяется для всех deleted заголовков, иначе они не рассматриваются. Например:
if (header_testname(h, US"X-Spam", 6, TRUE)) ...

uschar *lss_b64encode(uschar *cleartext, int length)
Это функция base64-кодирования строки, которая передает адрес и длину. Текст может содержать байты любого значения, включая ноль. Результат возвращается в динамическую память, которая динамически получается вызовом store_get(). Заканчивается нулем.

int lss_b64decode(uschar *codetext, uschar **cleartext)
Функция декодирования base64-строки. Аргументами являются заканчивающаяся нулем base64-строка и адрес переменной, который указывает на результат, находящийся в динамической памяти. Длина декодируемой строки получается после выполнения функции. Если вводимые данные неправильные, то результат -1. Нулевой байт добавляется в конце выводимой строки для более простого ее определения, как С-строки (предполагается, что она не содержит собственных нулей). Добавляемый нулевой байт не считается.

int lss_match_domain(uschar *domain, uschar *list)
Функция проверяет совпадения в доменном списке. Домены всегда выбираются бессистемно. Возвращемое значение одно из следующих:
OK      match succeeded
FAIL    match failed
DEFER   match deferred

Deffer обычно вызван каким-либо поиском, таким как невозможность связаться с базой данных.

int lss_match_local_part(uschar *localpart, uschar *list, BOOL caseless)
Функция проверяет совпадения в локальном списке. Третий аргумент контролирует чувствительность к регистру. Возвращаемое значение такое же, как и для lss_match_domain().

int lss_match_address(uschar *address, uschar *list, BOOL caseless)
Эта функция проверяет совпадения для списка адресов. Третий аргумент контролирует чувствительность к регистру. Домены всегда выбираются бессистемно. Возвращаемое значение такое же, как и для lss_match_domain().

int lss_match_host(uschar *host_name, uschar *host_address, uschar *list)
Функция проверяет совпадения в списке хостов. Самое распространенное использование:
lss_match_host(sender_host_name, sender_host_address, ...)

Пустое поле адреса совпадает с пустой записью в списке хостов. Если имя хоста NULL, соответствие названия $sender_host_address ищется автоматически, если название хоста должно совпадать с именем в списке. Возвращаемые значения такие же, как и в lss_match_domain(), но в дополнении lss_match_domain() возвращается ERROR в случае, когда имя искалось и не нашлось.

void log_write(unsigned int selector, int which, char *format, ...)
Эта функция записывает протоколы Exim. Первым аргументом должен быть селектор (это нужно для log_selector). Следующий аргумент должен быть LOG_MAIN, LOG_REJECT, LOG_PANIC или включая or комбинация аргументов. Это определяет, в какой протокол (может быть и в несколько) будет записано сообщение. Оставшиеся аргументы задают формат и вставку. Строка не должна включать символов новой строки даже в конце.

void receive_add_recipient(uschar *address, int pno)
Эта функция добавляет дополнительного получателя к сообщению. Первый аргумент задает адрес получателя. Если адрес не квалифицирован (не имеет домена), он квалифицируется с доменом qualify_recipient. Второй аргумент должен быть всегда -1.

Функция не позволяет Вам определить частный адрес errors_to (как описано в структуре recipient_item выше) потому, что это предшествует дополнению поля к структуре. Однако, впоследствии легко добавить это значение. Например:
receive_add_recipient(US"monitor@mydom.example", -1);
recipients_list[recipients_count-1].errors_to =
   US"postmaster@mydom.example";

BOOL receive_remove_recipient(uschar *recipient)
Эта удобная функция для удаления названного получателя из списка получателей. Возвращаемое значение TRUE, если получатель удален, FALSE, если совпадающий получатель не найден. Аргумент должен быть полным e-mail адресом.

uschar rfc2047_decode(uschar *string, BOOL lencheck, uschar *target, int zeroval, int *lenptr, uschar **error)
Эта функция декодирует строку, которая закодирована согласно RFC 2047. Обычно это содержимое файлов заголовка. Сперва каждое закодированное слово декодируется из Q или B кодировки в байтовую строку. Затем, если представлено имя таблицы кодировки и доступна функция iconv(), предпринимается попытка перевести результаты к данной кодовой таблице. Если это сделать не удается, бинарная строка возвращает сообщение об ошибке.

Первый аргумент: строка, которая должна быть дешифрована. Если lencheck TRUE, устанавливается максимальная длина MIME-слова. Третий агрумент: перекодированное слово или NULL, если перекодировка не удалась.

Если бинарный ноль попадается в строке, то он заменяется в соответствии с содержимым аргумента zeroval. Для использования с заголовками Exim значение должно быть не ноль, поскольку строки заголовков заканчиваются нулем.

Функция возвращает результат обработки строки, заканчивающийся нулем, если lenptr не NULL, длина обработки устанавливается в переменную, на которую она указывает. Когда zeroval равен 0, lenptr не должен быть NULL.

Если возникла ошибка, функция возвращает NULL и использует аргумент error для возврата сообщения об ошибке. Переменная, указывающая на error, устанавливается в NULL, если не было ошибки, она может быть установлена в не NULL даже когда функция возвращает не NULL-значение при удачной расшифровке, но есть проблемы с перекодировкой.

int smtp_fflush(void)
Функция используется совместно с smtp_printf(), как описано ниже.

void smtp_printf(char *, ...)
Аргументы такие же, как и у printf(): она записывает в выходной поток SMTP. Вы должны использовать эту функцию только когда есть выходной SMTP-поток, то есть, когда получается через SMTP входящее сообщение, в этом случае smtp_input TRUE, а smtp_batched_input FALSE. Если нужно протестировать сообщение с другого хоста (в противоположность локальному процессу, который использует опцию командной строки -bs), можете проверить значение sender_host_address, которое не NULL, если применяется удаленный хост.

Если SMTP-соединение установлено, smtp_printf() использует функцию вывода TLS, таким образом это может использоваться для всех SMTP-соединений.

Строки, которые написаны smtp_printf() внутри local_scan(), должны начинаться с правильного кода ответа: 550, если Вы собираетесь возвратить LOCAL_SCAN_REJECT, 451, если Вы собираетесь возвратить LOCAL_SCAN_TEMPREJECT и 250 в остальных случаях. Поскольку Вы пишете начальные строки многострочного сообщения, код может сопровождаться дефисом, чтобы показать, что это не последняя строка в коде отклика. Вы должны также убедиться, что строки, которые Вы пишете заканчиваются CRLF. Например:
smtp_printf("550-this is some extra info\r\n");
return LOCAL_SCAN_REJECT;

Учтите, что Вы можете создать многострочный отклик включая символы новой строки в данные, возвращаемые через аргумент return_text. Добавляемое значение использует smtp_printf() для того, чтобы Вы могли ввести задержки между многократным выводом.

Функция smtp_printf() не использует никакого возвращаемого сообщения об ошибке, потому что она не стирает автоматически идущий вывод и поэтому не проверяет состояние потока (в главном коде Exim стирание и проверка ошибок отрабатываются, когда Exim готов для следующего SMTP-соединения). Если Вы хотите удалить вывод и проверить ошибки (например, сбрасывая TCP/IP-соединение), Вы все еще сможете вызвать smtp_fflush(), у которой нет аргументов. Она стирает вывод и возвращает ненулевое значение при возникновении ошибки.

void *store_get(int)
Эта функция получает доступ к управлению внутренней памятью Exim. Она получает новый кусок памяти, чей размер задан аргументом. Exim завершается, если память исчерпана. Смотрите следующий раздел, где обсуждается выделение памяти.

void *store_get_perm(int)
Функция наподобии store_get(), но всегда получает память из постоянного пула. Смотрите следующий раздел, где обсуждается выделение памяти.

uschar *string_copy(uschar *string)
Смотрите ниже.

uschar *string_copyn(uschar *string, int length)
Смотрите ниже.

uschar *string_sprintf(char *format, ...)
Эти три функции создают строки, используемые динамической памятью Exim. Первая делает копию всей строки. Вторая копирует максимальное число символов, обозначенных во втором аргументе. Третья использует формат и вставку новой строки. В каждом случае результатом является указатель на новую строку в данном пуле памяти. Смотрите следующий раздел, где обсуждается выделение памяти.

41.8. Больше об обработке памяти Exim

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

Поскольку память передается, нормальная динамическая память не может быть использована для хранения данных, которые должны быть сохранены более, чем число входящих сообщений на том же SMTP-соединении. Однако, Exim на самом деле использует два пула динамической памяти, второй не передается и может использоваться для этих целей.

Если Вы хотите выделить память, которая останется доступной для последующих сообщений в том же SMTP-соединении, Вы должны установить:
store_pool = POOL_PERM
прежде, чем вызовите функцию выделения памяти. Не нужно восстанавливать значение без необходимости, однако, если Вы хотите вернуться к нормальному пулу, можете восстановить прежнее значение store_pool или установить явным образом POOL_MAIN.

Установка пула обращается ко всем функциям, которые получают динамическую память, включая expand_string(), store_get() и string_xxx(). Есть также удобная функция, называемая store_get_perm(), которая получает блоки памяти из постоянного пула, сохраняя значения store_pool.

Поиск

 

Найди своих коллег!