KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Хэл Фултон - Программирование на языке Ruby

Хэл Фултон - Программирование на языке Ruby

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Хэл Фултон, "Программирование на языке Ruby" бесплатно, без регистрации.
Перейти на страницу:

# Предположим, что mailtext содержит текст сообщения.


filename = "new.gif"

bin = File.read(filename)

encoded = [bin].pack("u")


mailtext << "begin 644 #{filename}"

mailtext << encoded

mailtext << "end"

# ...

На принимающей стороне мы должны извлечь закодированную информацию и декодировать ее методом unpack:

# ...

# Предположим, что 'attached' содержит закодированные данные

# (включая строки begin и end).


lines = attached.split("n")

filename = /begin ddd (.*)/.scan(lines[0]).first.first

encoded = lines[1..-2].join("n")

decoded = encoded.unpack("u") # Все готово к записи в файл.

Современные почтовые клиенты работают с почтой в формате MIME; даже текстовая часть сообщения обернута в конверт (хотя клиент удаляет все заголовки, прежде чем показать сообщение пользователю).

Подробное рассмотрение формата MIME заняло бы слишком много места, да и не относится к рассматриваемой теме. Но в следующем простом примере показано, как можно закодировать и отправить сообщение, содержащее текстовую часть и двоичное вложение. Двоичные куски обычно представлены в кодировке base64:

require 'net/smtp'


def text_plus_attachment(subject, body, filename)

 marker = "MIME_boundary"

 middle = "--#{marker}n"

 ending = "--#{middle}--n"

 content = "Content-Type: Multipart/Related; " +

  "boundary=#{marker}; " +

  "typw=text/plain"

 head1 = <<-EOF

MIME-Version: 1.0

#{content}

Subject: #{subject}

 EOF

 binary = File.read(filename)

 encoded = [binary].pack("m") # base64

 head2 = <<EOF

Content-Description: "#{filename}"

Content-Type: image/gif; name="#{filename}"

Content-Transfer-Encoding: Base64

Content-Disposition: attachment; filename="#{filename}"

 EOF


 # Возвращаем...

 head1 + middle + body + middle + head2 + encoded + ending

end


domain = "someserver.com"

smtp = "smtp.#{domain}"

user, pass = "elgar","enigma"


body = <<EOF

Это мое сообщение. Особо

говорить не о чем. Я вложил

небольшой GIF-файл.


          -- Боб

EOF

mailtext = text_plus_attachment("Привет...",body,"new.gif")


Net::SMTP.start(smtp, 25, domain, user, pass, :plain) do |mailer|

 mailer.sendmail(mailtext, ' [email protected]',

  [' [email protected]'])

end

18.2.7. Пример: шлюз между почтой и конференциями

В онлайновых сообществах общение происходит разными способами. К наиболее распространенным относятся списки рассылки и конференции (новостные группы).

Но не каждый хочет подписываться на список рассылки и ежедневно получать десятки сообщений; кто-то предпочитает время от времени заходить в конференцию и просматривать новые сообщения. С другой стороны, есть люди, которым система Usenet кажется слишком медлительной — они хотели бы видеть сообщение, пока еще электроны не успели остыть.

Таким образом, мы имеем ситуацию, когда в сравнительно небольшом закрытом списке рассылки рассматриваются те же темы, что в немодерируемой конференции, открытой всему миру. В конце концов кому-то пришла в голову мысль организовать зеркало — шлюз между обеими системами.

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

Эта задача была решена Дэйвом Томасом (Dave Thomas) — конечно, на Ruby, — и с его любезного разрешения мы приводим код в листингах 18.6 и 18.7.

Но сначала небольшое вступление. Мы уже немного познакомились с тем, как отправлять и получать электронную почту, но как быть с конференциями Usenet? Доступ к конференциям обеспечивает протокол NNTP (Network News Transfer Protocol — сетевой протокол передачи новостей). Кстати, создал его Ларри Уолл (Larry Wall), который позже подарил нам язык Perl.

В Ruby нет «стандартной» библиотеки для работы с NNTP. Однако один японский программист (известный нам только по псевдониму greentea) написал прекрасную библиотеку для этой цели.

В библиотеке nntp.rb определен модуль NNTP, содержащий класс NNTPIO. В этом классе имеются, в частности, методы экземпляра connect, get_head, get_body и post. Чтобы получить сообщения, необходимо установить соединение с сервером и в цикле вызывать методы get_head и get_body (мы, правда, немного упрощаем). Чтобы отправить сообщение, нужно сконструировать его заголовки, соединиться с сервером и вызвать метод post.

В приведенных ниже программах используется библиотека smtp, с которой мы уже познакомились. В оригинальной версии кода производится также протоколирование хода процесса и ошибок, но для простоты мы эти фрагменты опустили.

Файл params.rb нужен обеим программам. В нем описаны параметры, управляющие всем процессом зеркалирования: имена серверов, имена пользователей и т.д. Ниже приведен пример, который вы можете изменить самостоятельно. (Все доменные имена, содержащие слово «fake», очевидно, фиктивные.)

# Различные параметры, необходимые шлюзу между почтой и конференциями.


module Params

 NEWS_SERVER = "usenet.fake1.org"      # Имя новостного сервера.

 NEWSGROUP = "comp.lang.ruby"          # Зеркалируемая конференция.

 LOOP_FLAG = "X-rubymirror: yes"       # Чтобы избежать циклов.

 LAST_NEWS_FILE = "/tmp/m2n/last_news" # Номер последнего прочитанного

                                       # сообщения.

 SMTP_SERVER = "localhost"             # Имя хоста для исходящей почты.


 MAIL_SENDER = " [email protected]"      # От чьего имени посылать почту.

 # (Для списков, на которые подписываются, это должно быть имя

 # зарегистрированного участника списка.)


 mailing_list = " [email protected]"       # Адрес списка рассылки.

end

Модуль Params содержит лишь константы, нужные обеим программам. Большая их часть не нуждается в объяснениях, упомянем лишь парочку. Во-первых, константа LAST_NEWS_FILE содержит путь к файлу, в котором хранится идентификатор последнего прочитанного из конференции сообщения; эта «информация о состоянии» позволяет избежать дублирования или пропуска сообщений.

Константа LOOP_FLAG определяет строку, которой помечаются сообщения, уже прошедшие через шлюз. Тем самым мы препятствуем возникновению бесконечной

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

Возникает вопрос: «А как вообще почта поступает в программу mail2news?» Ведь она, похоже, читает из стандартного ввода. Автор рекомендует следующую настройку: сначала в файле .forward программы sendmail вся входящая почта перенаправляется на программу procmail. Файл .procmail конфигурируется так, чтобы извлекать сообщения, приходящие из списка рассылки, и по конвейеру направлять их программе mail2news. Уточнить детали можно в документации, сопровождающей приложение RubyMirror (в архиве RAA). Если вы работаете не в UNIX, то придется изобрести собственную схему конфигурирования.

Ну а все остальное расскажет сам код, приведенный в листингах 18.6 и 18.7.

Листинг 18.6. Перенаправление почты в конференцию

# mail2news: Принимает почтовое сообщение и отправляет

# его в конференцию.


require "nntp"

include NNTP


require "params"


# Прочитать сообщение, выделив из него заголовок и тело.

# Пропускаются только определенные заголовки.


HEADERS = %w{From Subject References Message-ID

 Content-Type Content-Transfer-Encoding Date}


allowed_headers = Regexp.new(%{^(#{HEADERS.join("|")}):})


# Прочитать заголовок. Допускаются только некоторые заголовки.

# Добавить строки Newsgroups и X-rubymirror.


head = "Newsgroups: #{Params::NEWSGROUP}n"

subject = "unknown"

while line = gets

 exit if line /^#{Params::LOOP_FLAG}/о # Такого не должно быть!

 break if line =~ /^s*$/

 next if line =~ /^s/

 next unless line =~ allowed_headers


 # Вырезать префикс [ruby-talk:nnnn] из темы, прежде чем

 # отправлять в конференцию.

 if line =~ /^Subject:s*(.*)/

  subject = $1


  # Следующий код вырезает специальный номер ruby-talk

  # из начала сообщения в списке рассылки, перед тем

  # как отправлять его новостному серверу.

  line.sub!(/[ruby-talk:(d+)]s*/, '')

  subject = "[#$1] #{line}"

  head << "X-ruby-talk: #$1n"

 end

 head << line

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*