Скрипт для отправки сообщения по e-mail, с использованием протокола SMTP

В скрипте, опубликованном ранее, имеется функция отправки сообщения по e-mail. В этом посте хочу остановиться на отправке e-mail при помощи скрипта чуть-чуть подробнее.

Простейшая функция отправки сообщения, использующая  доступный для нас smtp-сервер будет выглядеть следующим образом:

////////////////////////////////////////////////////////////////////////////
// JScript  shs smtp_send_mail.js
//Этот скрипт предназначен для отправки e-mail с компьютера,
//на котором не установлен локальный SMTP-сервис/сервер
///////////////////////////////////////////////////////////////////////////
objEmail = WScript.CreateObject("CDO.Message");  //создаем объект CDO.Message
//
//Зададим значения для полей письма.
objEmail.From = "sender@dandelion.ru";  	//адрес отправителя
objEmail.To = "recipient@sunflower.ru"; 	//адрес получателя
objEmail.Subject = "This’s the test email"; 	//тема
objEmail.Textbody = "You may delete this mail"; //тело письма
//
//Зададим значения для полей конфигурации
with (objEmail.Configuration.Fields) {
	Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2; //посылать сообщения по сети, используя SMTP
	Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "mailserver"; //ip или DNS-имя smtp-сервера
	Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25; // порт smtp-сервера, на котором он принимает сообщения
	Update();
}
try {
	objEmail.Send();
}
catch(e) {
	WScript.Echo(e.number);
	WScript.Echo(e.message);
}

Для отправки сообщения мы используем объект COM-объекты Collaboration Data Objects (CDO):

 

Скрипт очень простой, т.к. выполняет очень простые действия: отправляет письмо на английском языке, используя smtp-сервер (без авторизации на  оном smtp-сервере). Зачастую, для выполнения простых административных задач (например, отправки каких-либо alert’ов) этого бывает достаточно. А что, если же нам захочется большего? Давайте попробуем усовершенствовать скрипт.

А что, если smtp-сервер требует аутентификацию (например, если в качестве smtp-сервера мы захотим использовать smtp.mail.ru, то мы не сможем это сделать без предварительной аутентификации)? Давайте попробуем аутентифицироваться, для этого нам потребуется добавить всего 3 строчки внутрь блока with{…}, для изменения объекта конфигурации, после чего этот блок кода примет следующий вид (добавленные строки отображаются на темном фоне):

with (objEmail.Configuration.Fields) {
	Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2; //посылать сообщения по сети (используя SMTP)
	Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "mailserver"; //ip или DNS-имя smtp-сервера
	Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25; // порт smtp-сервера
	Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1; // используем basic authentication
	Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = "username"; //имя пользователя
	Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "userpassword"; //пароль пользователя
	Update();
}

Пойдем дальше. Если мы попробуем отправить письмо, содержащие кириллицу…

objEmail.Subject = "это тестовое письмо";
objEmail.Textbody = "это тестовое письмо, его можно удалить";

…, то на принимающей стороне получим нечитабельную тарабарщину, т.к. по умолчанию текстовые части письма должны содержать простой текст в кодировке  US-ASCII. Чтобы исправить положение, мы должны указать кодировку письма. Так, для передачи сообщений на русском языке, мы должны выбрать одну из следующих кодировок: “windows-1251”, “koi8-r”, “utf-7” или “utf-8”, что мы и сделаем, задав значение поля Charset:

objEmail.BodyPart.CharSet = "utf-8";

Ну, и наконец, если нам потребуется добавить к e-mail какие-либо файлы, в качестве вложений, то для этого используем метод AddAttachment, например, так:

objEmail.AddAttachment("c:\\nagent_log.txt");

В результате, получим такой скрипт:

////////////////////////////////////////////////////////////////////////////
// JScript  shs smtp_send_mail.js
//Этот скрипт предназначен для отправки e-mail с компьютера,
//на котором не установлен локальный SMTP-сервис/сервер
///////////////////////////////////////////////////////////////////////////
objEmail = WScript.CreateObject("CDO.Message");  //создаем объект CDO.Message
//
//Зададим значения для полей письма.
objEmail.From = "sender@dandelion.ru";  	//адрес отправителя
objEmail.To = "recipient@sunflower.ru"; 		//адрес получателя
objEmail.BodyPart.CharSet = "utf-8";               //задаем кодовую страницу сообщения
objEmail.Subject = "это тестовое письмо"; 	//тема письма
objEmail.Textbody = "это тестовое письмо, его можно удалить";  //тело письма
objEmail.AddAttachment("c:\\nagent_log.txt");	//добавляем к письму вложение: файл c:\nagent_log.txt
//
//Зададим значения для полей конфигурации
with (objEmail.Configuration.Fields) {
	Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2; //посылать сообщения по сети, используя SMTP
	Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "mailserver"; //ip или DNS-имя smtp-сервера
	Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25; // порт smtp-сервера
	Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1; // используем basic authentication
	Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = "username"; //имя пользователя
	Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "userpassword";  //пароль пользователя
	Update();
}
try {
	objEmail.Send();
}
catch(e) {
	WScript.Echo(e.number);  //выводим номер ошибки
	WScript.Echo(e.message); //выводим соощение об ошибке
}

PS Во время работы со скриптом столкнулся с тем, что он без проблем работал с почтовым сервером, находящимся внутри локальной сети, но никак не хотел работать с внешними smtp-серверами (smtp.mail.ru, smtp.yandex.ru и т.п.). Скрипт выдавал ошибку с номером -2147220973 (если перевести в hex, то получим 0x80040213). Погуглив, обнаружил, что такая ошибка по-английски звучит, как «The transport failed to connect to the server» и возникает, зачастую, как и в моем случае, по непонятным причинам.
Я «стопятьсот» раз перепроверил скрипт и настройки компьютера и знал, что нигде не ошибся: имя smtp-сервера, имя пользователя и пароль были указаны правильно, никакой файервол не перекрывает доступ во внешний мир, но скрипт упорно вываливался с этой ошибкой. Тогда я попробовал запустить его на проблемной машине от имени другой учетной записи с правами локального администратора – скрипт отработал без ошибок! Я уже было решил, что где-то перестарался и слишком сильно «завинтил гайки» с SRP и NTFS permissions, но в логах ничего подозрительного не обнаружил. Тогда я выдал учетной записи, под которой запускал скрипт, права локального администратора, но это не возымело никакого эффекта – скрипт, по-прежнему, завершался аварийно с ошибкой 0x80040213! Стало понятно, что проблема – в профиле пользователя.
Дальнейший «разбор полетов» не производил и просто убил проблемный профиль пользователя. После этого скрипт стал работать, как положено, и больше не выдавал сообщений о мифической невозможности соединиться с smtp-сервером.

40 Comments

  1. Pingback: Скрипт для автоматизации отправки файла cons*.* на заданный email-адрес по электронной почте « ShS's Blog

  2. День добрый.
    Парюсь с этим скриптом – никак не могу заставить его работать. Если отправлять файл из корневого каталога, то все нормально. А если в путь добавляется хоть один каталог, то начинает вываливаться ошибка “Синтаксическая ошибка в имени файла, имени папки или метке тома” (строка 14, символ 1).
    Не подскажете в чем может быть дело?

    ////////////////////////////////////////////////////////////////////////////
    // JScript shs smtp_send_mail.js
    //Этот скрипт предназначен для отправки e-mail с компьютера,
    //на котором не установлен локальный SMTP-сервис/сервер
    ///////////////////////////////////////////////////////////////////////////
    objEmail = WScript.CreateObject(“CDO.Message”); //создаем объект CDO.Message
    //
    //Зададим значения для полей письма.
    objEmail.From = “pk@domen.ru”; //адрес отправителя
    objEmail.To = “pk@domen.ru”; //адрес получателя
    objEmail.BodyPart.CharSet = “utf-8”; //задаем кодовую страницу сообщения
    objEmail.Subject = “это тестовое письмо”; //тема письма
    objEmail.Textbody = “это тестовое письмо, его можно удалить”; //тело письма
    objEmail.AddAttachment(“d:\\ftp\komplekt\test.txt”); //добавляем к письму вложение
    //
    //Зададим значения для полей конфигурации
    with (objEmail.Configuration.Fields) {
    Item(“http://schemas.microsoft.com/cdo/configuration/sendusing”) = 2; //посылать сообщения по сети, используя SMTP
    Item(“http://schemas.microsoft.com/cdo/configuration/smtpserver”) = “mail.domen.ru”; //ip или DNS-имя smtp-сервера
    Item(“http://schemas.microsoft.com/cdo/configuration/smtpserverport”) = 25; // порт smtp-сервера

    Update();
    }
    try {
    objEmail.Send();
    }
    catch(e) {
    WScript.Echo(e.number); //выводим номер ошибки
    WScript.Echo(e.message); //выводим соощение об ошибке
    }

  3. 2 PK:
    это от того, что у вас в 14 строке написано d:\\ftp\komplekt\test.txt,
    а должно быть d:\\ftp\\komplekt\\test.txt
    это же бубль гум JScript и в нем символ “\” является служебным, а, значит, его необходимо esc’ить, причем esc’ится он при помощи самого себя. Поэтому, если вы хотите, чтобы в вашей строке присутствовал символ “\”, то его надо писать два раза подряд: “\\”.

  4. подскажите, какую строчку необходимо добавить, чтобы приходило уведомление о прочтении письма на конкретный ящик

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

  5. Добрый день!

    Подскажите, пожалуйста, есть ли возможность отправляя письма с помощью CDO автоматически указывать адрес отправителя? Например исходя из windows авторизации…
    Чтобы письмо подписывалось адресом отправителя outlook.

    Спасибо,
    Иван

    • Возможность есть, только CDO, тут, наверное, не поможет Думаю, что вам надо так модифицировасть скрипт, чтобы он
      1)определял дефолтный почтовый клиент
      2)определял дефолтную учетную запись в клиенте
      3)определял почтовый адрес дефолтной учетной записи

  6. Подскажите на примере как добавить отчет о прочтении (на VBS всё работает на js не получается)

  7. .VBS

    Set objEmail = CreateObject("CDO.Message")

    Const SMTP_SERVER = "10.3.64.15"
    Const CDO_SUCCESS = 4
    Const CDO_FAIL = 2
    Const CDO_DELAY = 8
    Const CDO_SUCCESS_FAIL_DELAY = 14

    objEmail.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    objEmail.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = SMTP_SERVER
    objEmail.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
    objEmail.Configuration.Fields.Update

    with objEmail
    .From = "support@mydomen"
    .To = "relay@todomen"
    .Subject = "A Test"
    .Textbody = "A test script"
    .Fields("urn:schemas:mailheader:return-receipt-to") = "support@mydomen"
    .DSNOptions = CDO_SUCCESS_FAIL_DELAY
    .Fields.update
    .Send
    end with

    .js

    function SendEmail(MailServer, FromUser, ToUser, Subject, Msg, Attachment)
    { try {
    var iMsg = new ActiveXObject("CDO.Message");
    iMsg.To = ToUser;
    iMsg.From = FromUser;
    iMsg.Subject = Subject;

    if (typeof(Msg)!="undefined") iMsg.TextBody = Msg
    else iMsg.TextBody = "";
    iMsg.Configuration.fields("http://schemas.microsoft.com/cdo/configuration/sendusing")=3;
    iMsg.Configuration.Fields("http://schemas.microsoft.com/cdo/configuration/smtpserver")=MailServer;
    iMsg.Configuration.Fields("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 60;
    iMsg.Configuration.Fields("http://schemas.microsoft.com/cdo/configuration/languagecode") = 1049;
    iMsg.Configuration.Fields("urn:schemas:mailheader:disposition-notification-to") = "support@mydomen";
    iMsg.Configuration.Fields("urn:schemas:mailheader:return-receipt-to") = "support@mydomen";

    iMsg.Configuration.Fields.Update();
    iMsg.BodyPart.ContentTransferEncoding="quoted-printable";
    if (typeof(Attachment)!="undefined")
    if (Attachment!="")
    iMsg.AddAttachment(Attachment);
    iMsg.Send();
    }catch(e){ WScript.Quit(1);
    }
    }
    SendEmail("10.3.64.15","support@mydomen", "relay@todomen","A test","A test msg","");

    В js в заголовке письма нет признака

    • не вижу в вашем JScript скрипте кода, который бы выполнял действия соответствующие следующему куску кода на VBScript:
      with objEmail
      .From = “support@mydomen”
      .To = “relay@todomen”
      .Subject = “A Test”
      .Textbody = “A test script”
      .Fields(“urn:schemas:mailheader:return-receipt-to”) = “support@mydomen”
      .DSNOptions = CDO_SUCCESS_FAIL_DELAY
      .Fields.update
      .Send
      end with

      Именно присваетвается значение для DSNOptions (http://msdn.microsoft.com/en-us/library/aa487623(EXCHG.65).aspx) Delivery Status Notification (DSN) options for the message

      Добавьте в код на JScript аналогичную(ые) строку(и) и будет вам счастье:
      iMsg.DSNOptions = 14;

      • Да, естественно, и такую строчку надо будет добавить
        iMsg.Fields(“urn:schemas:mailheader:return-receipt-to”) = “support@mydomen”
        чтобы принимающая сторона знала, куда направлять уведомление

  8. VBS Отправитель получает и уведомление о прочтении и о доставке

    Set objEmail = CreateObject("CDO.Message")

    Const SMTP_SERVER = "10.3.64.15"
    Const From_m = "support@fromdomen"
    Const To_m = "relay@todomen"

    objEmail.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
    objEmail.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/smtpserver") = SMTP_SERVER
    objEmail.Configuration.Fields.Item _
    ("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
    objEmail.Configuration.Fields.Update

    with objEmail
    .From = From_m
    .To = To_m
    .Subject = "A Test"
    .Textbody = "A test script"
    .Fields("urn:schemas:mailheader:return-receipt-to") = From_m
    .DSNOptions = 14
    .Fields.update
    .Send
    end with

    Js Отправитель получает только уведомление о доставке (о прочтении нет)

    { try {
    var iMsg = new ActiveXObject("CDO.Message");

    iMsg.From = "support@fromdomen";
    iMsg.To = "relay@todomen";
    iMsg.Subject = "A test";
    iMsg.TextBody = "A test body";
    iMsg.Fields("urn:schemas:mailheader:return-receipt-to") = "suppoert@fromdomen";
    iMsg.DSNOptions = 14;

    iMsg.Configuration.fields("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2;
    iMsg.Configuration.Fields("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "10.3.64.15";
    iMsg.Configuration.Fields("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25;
    iMsg.Configuration.Fields.Update();

    iMsg.Send();
    }catch(e){ WScript.Quit(1);
    }}

  9. Подскажите, пожалуйста, что надо добавить в этот код на VBS, чтобы, при отсутствии активного подключения к Internet, Windows НЕ показывал ошибку, а просто закрывал программу?

    Const EmailFrom = “mail”
    Const EmailPassword = “***”
    Const strSmtpServer = “smtp”
    Const EmailTo = “mail”
    Set objEmail = CreateObject(“CDO.Message”)

    objEmail.From = EmailFrom
    objEmail.To = EmailTo
    objEmail.Subject = “***”
    objEmail.Textbody = “***”
    objEmail.Configuration.Fields.Item (“http://schemas.microsoft.com/cdo/configuration/sendusing”) = 2
    objEmail.Configuration.Fields.Item (“http://schemas.microsoft.com/cdo/configuration/smtpauthenticate”) = 1
    objEmail.Configuration.Fields.Item (“http://schemas.microsoft.com/cdo/configuration/sendusername”) = EmailFrom
    objEmail.Configuration.Fields.Item (“http://schemas.microsoft.com/cdo/configuration/sendpassword”) = EmailPassword
    objEmail.Configuration.Fields.Item (“http://schemas.microsoft.com/cdo/configuration/smtpserver”) = strSmtpServer
    objEmail.Configuration.Fields.Item (“http://schemas.microsoft.com/cdo/configuration/smtpserverport”) = 25
    objEmail.Configuration.Fields.Update
    objEmail.Send
    WScript.Quit

  10. Подскажите, пожалуйста, приведённый Вами скрипт можно оформлять в

    и вставлять на страницу?
    А какой файл надо подключить к странице, чтобы заработала функция WScript.CreateObject(“CDO.Message”)?
    Спасибо!

    • > Подскажите, пожалуйста, приведённый Вами скрипт можно оформлять в и вставлять на страницу?
      Не понял вопроса. О какой странице речь?

  11. убрались теги автоматически из сообщения…
    в прошлом посте я писала: в теги скрипт язык=яваскрипт :-)

  12. Спасибо за ответ!!! :-)
    В простенькую html-страничку с формой, в которой вводится сообщение, имя и обратный адрес человека. Он нажимает кнопку “Отправить” и мы должны запустить вышеуказанный скрипт по отправке этого письма.
    Вышеуказанный скрипт, оформленный в теги скрипт язык=”яваскрипт” … /скрипт запускается
    Но объект WScript.CreateObject(“CDO.Message”) не создаётся :-(
    Вот я и думаю, а к какой библиотеке идёт обращение – её же надо подключить!

    • Честно говоря, я далек от веб-программирования (понятия не имею что и как там происходит). Этот скрипт я писал для отправки уведомлений из командных файлов.
      ЗЫ Да.., и это не JavaScript, а JScript (т.е. что-то MS’овское, а вовсе не Sun/Oracle)
      ЗЗЫ Если тупо в лоб отвечть на ваш вопрос, то этот скрипт используем COM-объект CDO.Message, значит надо найти dll, которая содержит этот объект.
      http://www.google.com/search?hl=en&q=cdo.message+dll&rlz=1I7GGNI_ruRU448

  13. Спасибо за ответ!!!
    Разберусь, как написать отправку письма на javascript – обязательно здесь напишу!

  14. Может вопрос глупый – а как отсылать файл по шаблону с сегодняшней датой – типа test%date%.txt ?

    • Очевидно, можно использовать объект Date для получения текущей даты, а затем использовать полученные данные для формирования имени файла.

      • Спасибо за ответ – для меня это сложновато(( я думал можно с помощью шаблонов или там маски.

  15. Подскажите как сделать чтобы этот скрипт после выполнения (успешной отправки почты) запускал определенную программу?

  16. Спасибо за интересные и полезные статьи. В JS полный ноль, так что возможно некоторые вопросы могут показаться глупыми. Запустил ваш скрипт, единственно закоментил строку с прикреплением файла и внес свои данные. Запускал так: скачал node.exe с сайта http://nodejs.org/download/, запихнул его в папку с скриптом и запустил через косоль с параметром в виде имени скрипта. В ответ вывалилось это:

    C:\script>node mail.js

    node.js:189
    throw e; // process.nextTick error, or ‘error’ event on first tick
    ^
    GetConsoleTitleW: ╩юф фюёЄєяр эхтхЁхэ.
    Error: EACCES, я┐╜я┐╜я┐╜я┐╜я┐╜я┐╜я┐╜я┐╜я┐╜я┐╜я┐╜я┐╜ я┐╜я┐╜я┐╜я┐╜я┐╜я┐╜. ‘C:\scri
    pt\mail.js’
    at Object.openSync (fs.js:221:18)
    at Object.readFileSync (fs.js:112:15)
    at Object..js (module.js:449:44)
    at Module.load (module.js:334:31)
    at Function._load (module.js:293:12)
    at Array. (module.js:463:10)
    at EventEmitter._tickCallback (node.js:181:26)

    Запускал на WIn2003R2, если это важно. В чем может быть проблема? Куда копать?

        • Интерпретатор JScript имеет место быть в любой версии windows из коробки: cscript или wscript.
          Запустить можно как любую другую программу, набрав командной строке < имя_скрипта>.js или < полный путь к файлу скрипта>.js. В этом случае скрипт будет запущен при помощи wscript, если хотите запустить при помощи консольного движка cscript, то его надо указывать явно, например: cscript < имя_скрипта>.js

  17. Добрый день!
    Я использовал данный код для отправки из VFP 9.0 письма с вложением (прикрепляю файл с расширением .dbf и его одноименные с расширениями .fpt и .tbk. Все отправляется нормально, но вот сам файл .dbf в процессе пересылки “портится” и его размер почему то увеличивается. Не подскажите, в чем может быть дело?

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

      • Еще было бы время разбираться… Мне программу отдавать в четверг, а там помимо этого еще кучу всего отладить надо. Ну, все равно спасибо!

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.