пятница, 14 декабря 2012 г.

Хеш-таблица HashTable вида ключ-значение в Delphi 7


Задача
использовать хеш-таблицу вида ключ-значение (где значение может быть integer, string или TObject)

Среда: Delphi 7

Решение
Код взял отюда
http://read.pudn.com/downloads76/sourcecode/p2p/289231/Hashes.pas__.htm

на код навела статья из stackoverflow
http://stackoverflow.com/questions/2866144/is-there-anything-like-a-map-or-a-hashtable-in-delphi-6

Вставил модуль в проект. Скомпилировалось сразу. Методы работы легко посмотреть в коде.


Альтернативное решение
преимущество: использует только стандартные библиотеки Delphi без необходимости использования сторонних библиотек
недостаток: решение только частного случая: хэш-таблица integer->string

Модуль: IniFiles
классы: TStringHash, THashedStringList

спасибо за совет xen2:
http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=113797&msg=13631299


Еще одно решение
(похоже на подходящее, но сам пока не попробовал)
Использовать ...Hash... из AcedUtils

http://acedutils.narod.ru/AcedUtils.htm

опять спасибо за совет  xen2
http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=113797&msg=13631895




вторник, 11 декабря 2012 г.

Оптимизация Битрикса на виртуальной машине на примере хостинга 1gb.ua

Ситуация
Готовлю 2 сайта на Битриксе в режиме мультисайтовости.  Хостинг-провайдер http://1gb.ua. Для нормальной работы мультисайтовости виртуальный хостинг не подходит, поэтому покупаю виртуальный сервер. Тариф "VZ-профи". Сам я не большой специалист по linux и apache.

Проблема
Апач работает нестабильно, даже при 2-х запросах: при попытке одновременного открытия сайтов из разных вкладок. вываливает или 
"Service Temporarily Unavailable
The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later."
Или ответ выдется, но разметка сайтов сбита.

Решение
(помогла служба поддержки 1gb.ua)
увеличить директиву MaxProcessors до 100, в файле /etc/apache2/modules.d/00_mpm.conf. 

Это решение помогло. Вопрос на текущий момент закрыт.

правда замер производительности Битрикса тестами Битрикса не очень хороший: 7.82

На будущее поддержка посоветовала смотреть такие материалы

pinba
http://habrahabr.ru/post/129042/ 
http://pinba.org/wiki/Main_Page 

atop
Используйте утилиту atop. В режиме сбора статистики atop запускается как демон и раз в N времени (обычно 10 мин) скидывает состояние в двоичный журнал. Потом по этому журналу atop'ом же (ключ -r и имя лог-файла) можно бегать вперёд-назад кнопками T и t, наблюдая показания atop'а с усреднением за 10 минут в любой интересный момент времени. 
2012-12-11 16:59 1Gb.ua: Ставите так: emerge atop, его мануал http://www.opennet.ru/man.shtml?topic=atop&category=1&russian=2 




понедельник, 3 декабря 2012 г.

UPC eCommerceConnect Интеграция на платформе .NET: Часть 2. Программирование

Это вторая часть цикла статей, посвященных интеграции UPC
ВСЕ ЧАСТИ МАТЕРИАЛА

Пример на ASP.NET MVC3
Теперь собственно этап программирования

что имеем:
1. на руках есть XXXXXXX.pem файл с приватным ключем, с помощью которого нужно по определенным правилом зашифровать сообщение
2. раздел документации: "Интерфейс взаимодействия. Руководство администратора торговой системы."
http://ecommerce.upc.ua/docs/shop_gateway_interface.pdf

3. тема на форуме "Пример подключения для платформы .net со всеми деталями"
http://forum.ecommerce.upc.ua/viewtopic.php?f=12&t=468
(скажу сразу, что пример помог, но часть процедур пришлось додумывать самостоятельно)

1. Готовим объект - шлюз
Этот объект нам нужен для отображения полей формы
Как-то так. Если где ошибся с типами, вопросы/пожелания/предложения принимаются.
public class objectEccGateway: MegaUtils_V3.objects.objectMegaBase
{
public int Version;
public string MerchantID;
public string TerminalID;
public long TotalAmount;
public int Currency;
public string Locale; //ru
public string OrderID;
public string PurchaseTime;
public string Gateway;
public string SD;
public string PurchaseDesc;

public string XID;
public string ProxyPanResponse;
public string ApprovalCode;
public int TranCode;
public Int64 Rrn;

public string Signature;
}

2. Готовим объект - параметры системы
В этом объекте я настраивал параметры системы UPC

public class objectCatalogPaymentSystemUPC
{
public string MerchantId;

public string TerminalId;

public string CertificateFile;

public string Gateway;

}

3. Готовим утилиты для работы с файлами ключей

для парсинга приватного ключа из *.pem файла
помогло решение из статьи
http://stackoverflow.com/questions/1162504/decrypting-with-private-key-from-pem-file-in-c-sharp-with-net-crypto-library/1162519#1162519
в статье есть ссылка на файл
http://www.jensign.com/opensslkey/opensslkey.cs

который я взял и полностью вставил в код

(правда одну строчку пришлось закомментиовать там было "(сертификат)UI" - .NET не распознал её как идентификатор. на работоспособности системы она не отразилась)

эта статья тоже ссылается на правильное решение
http://stackoverflow.com/questions/243646/how-to-read-a-pem-rsa-private-key-from-net

также помогла функция PEM отсюда
http://pages.infinit.net/ctech/20040812-0816.html

на эту функцию вела статья
http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvcs/thread/348e1909-4da9-4980-94fc-f412dce07690/

в конце концов код выглядел так:

public class RSACryptoUtils
{
public static string RSASign(string data, string KeyFile)
{

RSACryptoServiceProvider rsaCsp =
LoadCertificateFile(KeyFile);

if (rsaCsp == null) return String.Empty;

byte[] dataBytes = Encoding.UTF8.GetBytes(data);

byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA1");

return Convert.ToBase64String(signatureBytes);

}

public static byte[] PEM(string type, byte[] data)
{
string pem = Encoding.ASCII.GetString(data);
string header = String.Format("-----BEGIN {0}-----", type);
string footer = String.Format("-----END {0}-----", type);
int start = pem.IndexOf(header) + header.Length;
int end = pem.IndexOf(footer, start);
string base64 = pem.Substring(start, (end - start));
return Convert.FromBase64String(base64);
}


public static RSACryptoServiceProvider LoadCertificateFile(string filename)
{
using (FileStream fs = File.OpenRead(filename))
{
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);

if (data[0] != 0x30)
{
// maybe it's ASCII PEM base64 encoded ?
data = PEM("RSA PRIVATE KEY", data);
}
if (data != null)
return opensslkey.DecodeRSAPrivateKey(data);
//x509 = new X509Certificate2(data);

throw new ApplicationException("error");

}
//return x509;
throw new ApplicationException("error");
}
}
4. Готовим функцию, которая возвращает объект "поля формы"
/// <summary>
/// Подготовить поля веб-формы для отправки на сервер
/// </summary>
/// <param name="aUPCParams">параметры мерчанта</param>
/// <param name="aLocale">язык интерфейса "ru" или "en"</param>
/// <param name="aDocRecID">код докуента</param>
/// <param name="aTotalAmont">сумма доакмента</param>
/// <param name="aCurrencyRecID">код валюты 980-гривна</param>
/// <param name="aSessionID">код сессии System.Web.HttpContext.Current.Session.SessionID</param>
/// <returns></returns>
public static objects.objectEccGateway GetRequestFormFields(
objectCatalogPaymentSystemUPC aUPCParams,
string aLocale,
string aDocRecID,
double aTotalAmont,
int aCurrencyRecID,
string aSessionID
)
{
var obj = new tfoBookingUtils.PaymentSystem.UPC.objects.objectEccGateway()
{
Version = 1,
MerchantID = aUPCParams.MerchantId,
TerminalID = aUPCParams.TerminalId,
TotalAmount = (long)(aTotalAmont * 100),
Currency = aCurrencyRecID, //
Locale = aLocale,
OrderID = aDocRecID + "|" + DateTime.Now.ToString("yyMMddHHmmss"),
PurchaseTime = DateTime.Now.ToString("yyMMddHHmmss"),
Gateway = aUPCParams.Gateway,
SD = aSessionID,

PurchaseDesc = string.Format("Заказ {0} на сумму {1:N} грн.", aDocRecID, aTotalAmont)
};

string str = String.Format("{0};{1};{2};{3};{4};{5};{6};",
obj.MerchantID, obj.TerminalID, obj.PurchaseTime, obj.OrderID,
obj.Currency, obj.TotalAmount, obj.SD);
obj.Signature = RSACryptoUtils.RSASign(str, System.Web.HttpContext.Current.Server.MapPath(aUPCParams.CertificateFile));

return obj;
}
5. Готовим форму отправки запроса. отображаем поля формы во вьюшке

@model objectEccGateway;
...
<form action="@Model.Gateway" method="post" id="ecommerce_pay">
<input type="hidden" name="Version" value="@Model.Version" />
<input type="hidden" name="MerchantID" value="@Model.MerchantID" />
<input type="hidden" name="TerminalID" value="@Model.TerminalID" />
<input type="hidden" name="TotalAmount" value="@Model.TotalAmount" />
<input type="hidden" name="Currency" value="@Model.Currency" />
<input type="hidden" name="locale" value="@Model.Locale" />
<input type="hidden" name="SD" value="@Model.SD" />
<input type="hidden" name="OrderID" value="@Model.OrderID" />
<input type="hidden" name="PurchaseTime" value="@Model.PurchaseTime" />
<input type="hidden" name="PurchaseDesc" value="@Model.PurchaseDesc" />
<input type="hidden" name="Signature" value="@Model.Signature" />
<input type="submit" name="submit" value="Оплатить" />
</form>
...
6. Обрабтываем результат

В экшене-обработчике результата
пишеи примерно такое

ecc = new objectEccGateway()
{
MerchantID = Request.Params["MerchantID"],
OrderID = Request.Params["OrderID"],
//PurchaseTime = Convert.ToInt64(Request.Params["PurchaseTime"]),
SD = Request.Params["SD"],
TerminalID = Request.Params["TerminalID"],
ProxyPanResponse = Request.Params["ProxyPan"],
Currency = Convert.ToInt32(Request.Params["Currency"]),
ApprovalCode = Request.Params["ApprovalCode"],
Signature = Request.Params["Signature"],
Rrn = Convert.ToInt64(Request.Params["Rrn"]),
XID = Request.Params["XID"],
TranCode = Convert.ToInt32(Request.Params["TranCode"]),
TotalAmount = Convert.ToInt64(Request.Params["TotalAmount"])
};



if (Convert.ToInt32(Request.Params["TranCode"]) == 0)
{
//ok
}
else{
//error
}

UPC eCommerceConnect Интеграция на платформе .NET: Часть 3. Тестирование

Это третья часть цикла статей, посвященных интеграции UPC
ВСЕ ЧАСТИ МАТЕРИАЛА

Для тестирования пользуюсь информацией из этого файла

Интеграция и тестирование. Руководство разработчика торговой системы.

http://ecommerce.upc.ua/docs/testing.pdf

в частности для проверки корректного результата оплаты
использую такие параметры тестовой карточки

параметры для тестирования
#карты: 4999999999990011
действует до: 12/2012
cvv2: 999

UPC eCommerceConnect Интеграция на платформе .NET: Часть 1. Организационные вопросы

Это первая часть цикла статей, посвященных интеграции UPC

ВСЕ ЧАСТИ МАТЕРИАЛА




Для начала работы я завел свой тестовый аккаунт
1. Заявка на регистрацию

1. скачал отсюда http://ecommerce.upc.ua/docs.html
2. заполнил своими реквизитами (написал, что модуль нужен в тестовых целях)
3. отправил на адрес ec@upc.ua
Получаю ответ на заявку
(в моем случае менеджеры системы откликнулись быстро, это заняло порядка 15-ти минут)



Ответ выглядит примерно так:

Здравствуйте,

Тестовые данные:


Адрес шлюза : https://secure.upc.ua/ecgtest/enter
MerchantID=XXXXXXX

TerminalID=YYYYYYY
Интерфейс торговца :https://secure.upc.ua/ecgtest/merchant
Логин / Пароль :XXXXXXX/в следующем письме

Сертификат сервера - в аттаче

Также необходимо, чтобы Вы выслали сертификат торговца (файл с именем XXXXXXX.crt) на адрес ec@upc.ua

Вся необходимая документация: здесь: http://ecommerce.upc.ua/site/docs.html
Тестовые карты здесь: http://ecommerce.upc.ua/docs/Testing.pdf
(See attached file: test-server.cert)
В архиве batch.rar документация и примеры по формированию подписи.

(See attached file: batch.rar)
password: 1

где
XXXXXXX-код мерчанта
YYYYYYYY-код терминала
Согласно письма, следующее действие - подготовка ключей и сертификатов

2. Подготовка ключей и сертификатов
(эту часть делаем согласно readme-файла вложенного письма, только с поправкой на 64-х битность Windows-системы)
1. Выкачиваю OpenSSL-библиотеки
с сайта http://slproweb.com/products/Win32OpenSSL.html
Выкачиваю такие файлы
Visual C++ 2008 Redistributables (x64)
тут шла ссылка на сайт микрософт
Win64 OpenSSL v1.0.1c Light
тут версия 1.0.1с актуальна на 2012 12
2. Запускаю выкачанные инсталлянты
По умолчанию библиотеки OpenSSL установились в директорию C:\OpenSSL-Win64

3. Прописываю библиотеки в переменной Path
(пример для Windows 7 x64)
Панель управления\Все элементы панели управления\Система

дополнительные параметры системы

вкладка "Дополнительно" кнопка "Переменные среды"
Область "системные переменные"
переменная PATH

Добавляю тута путь: C:\OpenSSL-Win64\bin
4. правлю конфиг-файл config.dat
устанавливаю там параметры согласно заявке
(поля в разделе [req] не трогал)

5. Собственно гененрирую файлы ключей и сертификатов
для этого запускаю

run.bat XXXXXXX

где
XXXXXXX-мой код мерчанта
На выходе получаю файлы
XXXXXXX.pem – приватный ключ
XXXXXXX.pub – публичный ключ
XXXXXXX.crt – сертификат
6. Файл сертификата (*.crt) отправляю в UPC на адрес ec@upc.ua
7. Получаю ответ от UPC
В моем случае все было достаточно быстро и лаконично : "подгружен"
Еще я естествеено проверил их веб-интерфейс залогинившись под своим именем/паролем.
Пароль, кстати, система сразу предложила сменить.

3. Настройка путей ответа.
в веб-интерфейсе заходим в раздел "терминалы". выбираем редактированиее терминала.
и заполняем поля:
страницы для ответов сервера (эти страницы должны быть в вашем веб-приложении и содержать обработку ответов сервера).

UPC eCommerceConnect Интеграция на платформе .NET

Делал интеграцию платежной системы UPC на платформе .NET

Возможно мой опыт кому-то окажется полезен.

материала достаточно много, поэтому разбил на части

1. Орг вопросы
2. Программирование
3. Тестирование

Пожелания/предложения принимаются в комментариях.

ссылки по теме:
Сайт платежной системы
http://ecommerce.upc.ua/
Сайт организации-владельца системы
https://upc.ua/ua.htm
Документация по системе
http://ecommerce.upc.ua/docs.html
блог разработчика с полезной информацией
http://mac-blog.org.ua/upc-visa-mastercard-payment/
форум
http://forum.ecommerce.upc.ua/viewforum.php?f=9


среда, 31 октября 2012 г.

JQuery-слайдер на инфоблоках Битрикс

Задача
создание слайдера на главной странице, чтобы сам заказчик мог менять его содержимое

идея решения
внутренности делаем на инфоблоке битрикса
реализация собственно функционала слайдинга - прикручиваем какой-либо из JavaScript слайдеров (см. ссылки ниже)
интерфейсная часть: модификация шаблона компонента "Лента новостей"

Ключевые идеи взял из статьи (спасибо автору)
http://dkruban.ru/2012/07/sozdanie-slajdera-na-1s-bitriks/

Ссылки

слайдеры (смотрите демки, выбирайте, который нравится)


Lof JSliderNew
http://landofcoder.com/jquery-plugins/lof-jslidernew-plugin.html

BxSlider 
http://bxslider.com/

SlidesJS
http://slidesjs.com/
полезная статья применения простого jQuery-Слайдера
http://ivanpopov.ru/1s-bitriksprostoi-slaider-elementov-infobloka

понедельник, 3 сентября 2012 г.

Проблемы с установкой MS SQL Server 2008 Management studio

Проблема

Не устанавливалась SQL Server Management Studio. 
Детали: MS SQL Server 2008 Express установлен. А Management Studio к нему прикрутить не могу. 


Примечание

На самом деле Management Studio мне нужна была только для подключения к удаленным базам, поэтому и сам сервер мне по сути не был нужен. 
Нужна была только сама студия.


Решение 

Management Studio устанавливается только с первым экземпляром, SQL Server Express. Как расширение она уже не работает.
Поэтому рекомендуют "снести" текущий установленный экземпляр.

Я сносил из "Программ и компонентов" все что начинается с "MS SQL Server 2008 ..."


Ссылки

1. Management studio
Саму Management Studio скачивал отсюда
2. Статья на английском
Помогла статья: http://stackoverflow.com/questions/8917315/how-to-get-enterprise-manager-installed-in-sql-server-2008-express

четверг, 23 августа 2012 г.

MS SQL 2008: Client IP address on shared hosting


Task:
get IP-address of the client (IP-address of client connection)
(for example to write to the log address of the computer you are using to connect)


Surroundings and limitations:
MS SQL 2008 database is on a hosting without admin rights.
There are no rights to execute dm_exec_connections.

Solution
(key line highlighted)

select
   CONNECTIONPROPERTY('net_transport') AS net_transport,
   CONNECTIONPROPERTY('protocol_type') AS protocol_type,
   CONNECTIONPROPERTY('auth_scheme') AS auth_scheme,
   CONNECTIONPROPERTY('local_net_address') AS local_net_address,
   CONNECTIONPROPERTY('local_tcp_port') AS local_tcp_port,
   CONNECTIONPROPERTY('client_net_address') AS client_net_address 

Links

MSDN help

MS SQL 2008 IP адрес клиента

Задача: 
получить IP-адрес клиента (IP-дарес клиентского соединения)
(например для того чтобы записывать в лог адрес компьютера с которого производилось подключение)


Окружение и ограничения: 
база MS SQL 2008 на хостинге, прав администартора нет. Прав на процедуру dm_exec_connections нет.

Решение
(ключевую строчку выделил)


 select
   CONNECTIONPROPERTY('net_transport') AS net_transport,
   CONNECTIONPROPERTY('protocol_type') AS protocol_type,
   CONNECTIONPROPERTY('auth_scheme') AS auth_scheme,
   CONNECTIONPROPERTY('local_net_address') AS local_net_address,
   CONNECTIONPROPERTY('local_tcp_port') AS local_tcp_port,
   CONNECTIONPROPERTY('client_net_address') AS client_net_address 


Ссылки
Оригинал на английском
http://weblogs.sqlteam.com/peterl/archive/2010/03/20/Get-client-IP-address.aspx

Помощь в MSDN по свойству CONNECTIONPROPERTY
http://msdn.microsoft.com/en-us/library/bb895240.aspx

вторник, 21 августа 2012 г.

Bitrix - Дополнительные поля в почтовом уведомлении о новом заказе

Задача: добавить поля "комментарий киента к заказу", "телефон", "название службы доставки", "название способа оплаты", "полный адрес клиента" в шаблон письма-уведомления о новом заказе

Идея решения:
1. Добавляем новые поля в шаблон почтового события SALE_NEW_ORDER.
Поля добавляем в текст шаблона в виде
#<имя поля>#

2. Добавляем в систему обработчик события "перед отправкой почтового уведомления о новом заказе".
3. В обработчике события заполняем добавленные в шаблон поля

Решение
Шаг 1. Шаблон SALE_NEW_ORDER

В шаблон добавил такой текст

...
Контактный телефон: #PHONE#
Комментарий:
#ORDER_DESCRIPTION#
Служба доставки: #DELIVERY_NAME#
Способ оплаты: #PAY_SYSTEM_NAME#
Адрес доставки: #FULL_ADDRESS#
...

Шаги 2 и 3. Код

в /bitrix/php_interface/init.php

вписал такой код
Код
//-- Добавление обработчика события

AddEventHandler("sale", "OnOrderNewSendEmail", "bxModifySaleMails");

//-- Собственно обработчик события

function bxModifySaleMails($orderID, &$eventName, &$arFields)
{
  $arOrder = CSaleOrder::GetByID($orderID);
  
  //-- получаем телефоны и адрес
  $order_props = CSaleOrderPropsValue::GetOrderProps($orderID);
  $phone="";
  $index = ""; 
  $country_name = "";
  $city_name = "";  
  $address = "";
  while ($arProps = $order_props->Fetch())
  {
    if ($arProps["CODE"] == "PHONE")
    {
       $phone = htmlspecialchars($arProps["VALUE"]);
    }
    if ($arProps["CODE"] == "LOCATION")
    {
        $arLocs = CSaleLocation::GetByID($arProps["VALUE"]);
        $country_name =  $arLocs["COUNTRY_NAME_ORIG"];
        $city_name = $arLocs["CITY_NAME_ORIG"];
    }

    if ($arProps["CODE"] == "INDEX")
    {
      $index = $arProps["VALUE"];   
    }

    if ($arProps["CODE"] == "ADDRESS")
    {
      $address = $arProps["VALUE"];
    }
  }

  $full_address = $index.", ".$country_name."-".$city_name.", ".$address;

  //-- получаем название службы доставки
  $arDeliv = CSaleDelivery::GetByID($arOrder["DELIVERY_ID"]);
  $delivery_name = "";
  if ($arDeliv)
  {
    $delivery_name = $arDeliv["NAME"];
  }

  //-- получаем название платежной системы   
  $arPaySystem = CSalePaySystem::GetByID($arOrder["PAY_SYSTEM_ID"]);
  $pay_system_name = "";
  if ($arPaySystem)
  {
    $pay_system_name = $arPaySystem["NAME"];
  }

  //-- добавляем новые поля в массив результатов
  $arFields["ORDER_DESCRIPTION"] = $arOrder["USER_DESCRIPTION"]; 
  $arFields["PHONE"] =  $phone;
  $arFields["DELIVERY_NAME"] =  $delivery_name;
  $arFields["PAY_SYSTEM_NAME"] =  $pay_system_name;
  $arFields["FULL_ADDRESS"] = $full_address;   
}



Полезные ссылки

Прототип кода взял из комментария к статье "Артикул в уведомлении о заказе" Спасибо автору
http://dev.1c-bitrix.ru/community/webdev/user/46046/blog/articles-in-the-notification-of-the-order/
События интернет-магазина
Тут помог раздел "События связанные с отправкой почтовых шаблонов"
http://dev.1c-bitrix.ru/api_help/sale/sale_events.php
Курс разработчика, раздел "Обработка событий"
http://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=46&LESSON_ID=2222
Документация для разработчика, раздел "Обработка событий"
http://dev.1c-bitrix.ru/api_help/main/general/technology/events.php
Тема форума "Обработка событий"
http://dev.1c-bitrix.ru/community/forums/forum6/topic28942/

Delphi Indy 10 SSL and Encoding Issues


When sending mail through Indy components in Delphi 7 encountered periodically on different issues.
Variants of problems:


  • sending emails from machines that - there are Russian letters in the names
  • email using SSL
  • problems with encoding in the subject and body of the message


I didn't find common solution in internet - only scattered pieces.

All solutions brought here. May be it will be useful for someone (tested on russian charsets)

System configuration:

  • Delphi 7, 
  • Indy components version: 10.5.9.4797 (installed manually by article http://conferences.embarcadero.com/article/32160
    file: Indy10_4798.zip)
  • SSL Library: openssl-1.0.0d-i386-win32.zip
    from http://indy.fulgan.com/SSL/
    (files libeay32.dll and ssleay32.dll are copied to project output directory)

main procedure is - SendEmail
Main pieces of code are commented.

uses
...

IdCoderMIME , IdMessage, IdSMTP,
IdSSLOpenSSL , IdIOHandler, IdAttachmentFile, IdExplicitTLSClientServerBase, idCharSets, idText
...

type

  //-- helper stub-class for TidMessage.OnInitializeISO event  handling
  Tindystub = class(tobject)
  public
    procedure OnInitISO(var VHeaderEncoding: Char; var VCharSet: string);
  end;

procedure Tindystub.OnInitISO(var VHeaderEncoding: Char;
  var VCharSet: string);
begin
  VCharSet := IdCharsetNames[ idcs_UTF_8 ];
end;


procedure SendEmail(
    aSmtpServer: string;
    aSmtpPort: integer;
    aSmtpLogin: string;
    aSmtpPassword: string;
    aFromAddress: string;
    aFromName: string;
    aToAddress: string;
    aMessageSubject: string;
    aMessageText: string;
    aAttachmentFileFullName: string;
    aUseSSL: boolean = false
    );
var
  lMessage : TIDMessage;
  lSMTP    : TIdSMTP;
  attach   : TIdAttachmentFile;
  sslHandler: TIdSSLIOHandlerSocketOpenSSL;
  lindystub: Tindystub;
  idtTextPart: TIdText;
begin
  lMessage:= TIDMessage.Create(nil);
  lSmtp:= TIDSmtp.Create(nil);
  sslHandler:= TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  lindystub:= Tindystub.Create();
  try
    lSMTP.AuthType:= satDefault;

    lSMTP.Host     := aSmtpServer;   //Хост
    lSMTP.Port     := aSmtpPort;     //Порт (25 - по умолчанию)

    lSMTP.Username := aSmtpLogin;
    lSMTP.Password := aSmtpPassword;

    //-- solving "non-english computer name" problem
    lSMTP.HeloName := 'tfo';

    //-- using SSL
    if (aUseSSL) then
    begin
      SSLHandler.Destination := aSmtpServer+':'+IntToStr(aSmtpPort);
      SSLHandler.Host := aSmtpServer;
      SSLHandler.Port := aSmtpPort;
      SSLHandler.DefaultPort := 0;
      SSLHandler.SSLOptions.Method := sslvSSLv23;
      SSLHandler.SSLOptions.Mode := sslmUnassigned;
      lSMTP.IOHandler := SSLHandler;
      lSMTP.UseTLS := utUseImplicitTLS;
    end;

    //-- subject encoding
    lMessage.OnInitializeISO := lindystub.OnInitISO;

    lMessage.Subject      := aMessageSubject;
    lMessage.From.Address := aFromAddress;
    lMessage.From.Name    := aFromName;
    lMessage.Recipients.EMailAddresses:= aToAddress;

    //-- message body encoding
    idtTextPart := TIdText.Create(lMessage.MessageParts, nil);
    idtTextPart.ContentType := 'text/plain';
    idtTextPart.CharSet := 'Windows-1251';

    idtTextPart.Body.Text:= aMessageText;

    //-- work with attached files
    if (aAttachmentFileFullName <> '') then
    begin
      attach:=TIdAttachmentFile.Create(lMessage.MessageParts, aAttachmentFileFullName); //Вложение
    end;

    //-- sending email
    lSMTP.Connect;
    try
      lSMTP.Send(lMessage);
    finally
      lSMTP.Disconnect;
    end;

  finally
    sslHandler.Free;
    lMessage.Free;
    lSmtp.Free;
    lindystub.Free;
  end;
end;

Delphi Indy 10 SMTP Кодировки и SSL Решение все в одном


При отправке писем через SMTP компоненты Indy в Delphi 7 наталкивался периодически на разные проблемы.
Варианты проблем:

  • отправка писем от машин, у которых - есть русские буквы в названиях
  • отправка писем через SSL
  • проблемы с кодировками в теме и теле письма

В интернете общего решения не находил - только разрозненные части.

Все решения свел здесь. может кому-то пригодится

Конфигурация системы:

  • Delphi 7, 
  • Версия компонентов Indy:10.5.9.4797 (переустанавливал вручную по статье http://conferences.embarcadero.com/article/32160  файл: Indy10_4798.zip)
  • ssl - библиотеки: openssl-1.0.0d-i386-win32.zip
    брал отсюда http://indy.fulgan.com/SSL/
    (в директорию с exe-файлом подложил libeay32.dll и ssleay32.dll)


Ключевая процедура в коде - SendEmail
Важные места подкомментировал.

uses
...

IdCoderMIME , IdMessage, IdSMTP,
IdSSLOpenSSL , IdIOHandler, IdAttachmentFile, IdExplicitTLSClientServerBase, idCharSets, idText
...

type

  //-- вспомогательный класс-заглушка для подвязки события компонента indy
  Tindystub = class(tobject)
  public
    procedure OnInitISO(var VHeaderEncoding: Char; var VCharSet: string);
  end;

procedure Tindystub.OnInitISO(var VHeaderEncoding: Char;
  var VCharSet: string);
begin
  VCharSet := IdCharsetNames[ idcs_UTF_8 ];
end;


procedure SendEmail(
    aSmtpServer: string;
    aSmtpPort: integer;
    aSmtpLogin: string;
    aSmtpPassword: string;
    aFromAddress: string;
    aFromName: string;
    aToAddress: string;
    aMessageSubject: string;
    aMessageText: string;
    aAttachmentFileFullName: string;
    aUseSSL: boolean = false
    );
var
  lMessage : TIDMessage;
  lSMTP    : TIdSMTP;
  attach   : TIdAttachmentFile;
  sslHandler: TIdSSLIOHandlerSocketOpenSSL;
  lindystub: Tindystub;
  idtTextPart: TIdText;
begin
  lMessage:= TIDMessage.Create(nil);
  lSmtp:= TIDSmtp.Create(nil);
  sslHandler:= TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  lindystub:= Tindystub.Create();
  try
    lSMTP.AuthType:= satDefault;

    lSMTP.Host     := aSmtpServer;   //Хост
    lSMTP.Port     := aSmtpPort;     //Порт (25 - по умолчанию)

    lSMTP.Username := aSmtpLogin;
    lSMTP.Password := aSmtpPassword;

    //-- решаем проблему отправки писем, если у компьютера руссоке имя
    lSMTP.HeloName := 'tfo';

    //-- отправляем почту через SSL, если надо (пока тестировал только на ukr.net)
    if (aUseSSL) then
    begin
      SSLHandler.Destination := aSmtpServer+':'+IntToStr(aSmtpPort);
      SSLHandler.Host := aSmtpServer;
      SSLHandler.Port := aSmtpPort;
      SSLHandler.DefaultPort := 0;
      SSLHandler.SSLOptions.Method := sslvSSLv23;
      SSLHandler.SSLOptions.Mode := sslmUnassigned;
      lSMTP.IOHandler := SSLHandler;
      lSMTP.UseTLS := utUseImplicitTLS;
    end;

    //-- решаем вопрос кодировки темы письма
    lMessage.OnInitializeISO := lindystub.OnInitISO;

    lMessage.Subject      := aMessageSubject;
    lMessage.From.Address := aFromAddress;
    lMessage.From.Name    := aFromName;
    lMessage.Recipients.EMailAddresses:= aToAddress;

    //-- решаем вопрос кодировки тела письма
    idtTextPart := TIdText.Create(lMessage.MessageParts, nil);
    idtTextPart.ContentType := 'text/plain';
    idtTextPart.CharSet := 'Windows-1251';

    idtTextPart.Body.Text:= aMessageText;

    //-- работа со вложенными файлами
    if (aAttachmentFileFullName <> '') then
    begin
      attach:=TIdAttachmentFile.Create(lMessage.MessageParts, aAttachmentFileFullName); //Вложение
    end;

    //-- собственно отправка
    lSMTP.Connect;
    try
      lSMTP.Send(lMessage);
    finally
      lSMTP.Disconnect;
    end;

  finally
    sslHandler.Free;
    lMessage.Free;
    lSmtp.Free;
    lindystub.Free;
  end;
end;


Вопросы/пожелания/предложения оставляйте в комментариях

см. также

Обработка почты через POP3 компоненты Indy10 в Delphi 7
http://dev-doc.blogspot.com/2013/02/delphi-indy-10-pop3.html

EMAIL MESSAGES FROM DELPHI. USING INDY TO SEND E-MAILS
https://community.embarcadero.com/article/technical-articles/162-programming/5913-email-messages-from-delphi-using-indy-to-send-emails
---------------------------------
UPD
Если хотите отправить почту через ukr.net
проверьте чтобы были включен параметр "POP3 и SMTP" согласно статье
http://www.qdpro.com.ua/node/17758
(если не сделать, будет выскакивать ошибка SYSERR 417)