例题5-11 邮件传输代理的交互

题目链接:UVaoj 814 The Letter Carrier's Rounds

UVa814
题目翻译:

对于电子邮件应用程序,您将描述在成对MTA之间发生的基于SMTP的通信。发件人的用户代理向发送邮件传输代理(MTA)提供格式化邮件。发送MTA使用简单邮件传输协议(SMTP)与接收MTA通信。接收MTA将邮件传递给收件人的用户代理。通信链路初始化后,发送MTA将命令行(一次一行)发送给接收MTA,接收MTA在处理每个命令后返回三位编码的响应。发送方命令按每条消息的发送顺序显示如下。当同一消息发送到同一MTA的多个用户时,存在多个RCPT TO行。发送给不同MTA用户的邮件需要单独的SMTP会话。

HELO myname 向接收者标识发送者(是的,只有一个l)
MAIL FROM: 标识邮件发件人
RCPT TO: 标识消息的一个收件人
DATA 后接任意数量的文本行,包含消息正文,以第1列中包含句点的行结尾。
QUIT 终止通信

接收MTA发送以下响应代码:
221 关闭连接(退出后)
250 操作正常(MAIL FROM和RCPT TO发送邮件到指定可接受的用户或发生消息后)
354 开始发送邮件(在数据之后)
550 不接受指令;此处没有此类用户(在RCPT TO之后,未知用户)
输入:
输入包含MTA的描述,后跟任意数量的消息。每个MTA描述都以MTA名称及其名称(1到15个字母数字字符)开头。MTA名称后面是在该MTA接收邮件的用户数和用户列表(每个用户1到15个字母数字字符)。MTA描述以第1列中的星号终止。每条消息以发送用户的名称开始,后跟一个收件人标识符列表。每个标识符的格式都是user@mtaname。消息(每行不超过72个字符)以第1列中的星号开始和结束。第1列中带星号的行(而不是发送方和收件人列表)表示整个输入的结束。
输出:
对于每条消息,显示发送和接收MTA之间的通信。邮件中提到的每个MTA都是有效的MTA;但是,目标MTA中可能不存在邮件收件人。接收MTA通过使用550代码响应rcpt to命令来拒绝这些用户的邮件。拒绝不会影响在同一MTA上传递给授权用户。如果在特定MTA中没有至少一个授权收件人,则不会发送数据。只有一个SMTP会话用于向特定MTA的用户发送邮件。例如,发送给同一MTA中5个用户的邮件将只有一个SMTP会话。另外,一条消息只发送给同一个用户一次。发送方联系接收MTA的顺序与输入文件中的顺序相同。如示例输出所示,在通信前面加上正在通信的MTA名称,并缩进每个非空的通信行。不应打印内部空间。

思路:

本题的任务为模拟发送邮件时MTA(邮件传输代理)之间的交互。所谓MTA,就是email地址格式user@mtaname的“后面部分”。当某人从user1@mta1发送给另一个人user2@mta2时,这两个MTA将会通信。如果两个收件人属于同一个MTA,发送者的MTA只需与这个MTA通信一次就可以把邮件发送给这两个人。

本题可以用3个步骤解决:
1.储存用户地址:使用string类型的vector容器储存每个用户的完整地址。如:user@MTA
2.收件人MTA分类和顺序记录:定义一个vrctor(记录收件人MTA顺序)和map(MAT为键,string类型的vector容器(收件人地址)为值),对每一个收件人先分离MTA,在map中查MTA键值,若查得到则添加收件人地址到对应的值的vector容器中;若查不到则添加MTA为键string类型的vector容器为值到map中,添加这个MTA到记录顺序的vrctor中,再执行前一步操作。
3.输出:在map中依次使用 记录收件人MTA顺序的vretor容器 作为键值得到对应的 收件人地址vrctor容器,在第一步的用户地址中查找 收件人地址容器 里的每一个地址,找到则存在用户。再格式化输出即可

代码:
#include
#include
#include
#include
#include
using namespace std;

void parse_address(const string& s, string& user, string& mta) {        //分离邮件地址,得到名字和MTA
  int k = s.find('@');
  user = s.substr(0, k);
  mta = s.substr(k+1);
}

int main() {
  int k;
  string s, t, user1, mta1, user2, mta2;
  set addr;

  // 输入所有MTA,转化为地址列表
  while(cin >> s && s != "*") {
    cin >> s >> k;
    while(k--) { cin >> t; addr.insert(t + "@" + s); }      //储存所有用户完整地址
  }

  while(cin >> s && s != "*") {
    parse_address(s, user1, mta1);      //分离发件人地址

    vector mta;     //所有需要连接的mta,按照输入顺序
    map > dest;      //每个mta需要发送的用户
    set vis;        //记录收件人地址
    while(cin >> t && t != "*") {
      parse_address(t, user2, mta2);        //分离收件人地址
      if(vis.count(t)) continue;     //检查重复的收件人
      vis.insert(t);
      if(!dest.count(mta2)) { mta.push_back(mta2); dest[mta2] = vector(); }
      dest[mta2].push_back(t);      //相同的MTA用户归类为到一个列表
    }
    getline(cin, t); // 把"*"这一行的回车吃掉

    // 输入邮件正文
    string data;
    while(getline(cin, t) && t[0] != '*') data += "     " + t + "\n";

    for(int i = 0; i < mta.size(); i++) {       //对每个MTA按顺序输出SMTP会话
      string mta2 = mta[i];
      vector users = dest[mta2];        //读取该MTA下收件人地址
      cout << "Connection between " << mta1 << " and " << mta2 <\n";
      cout << "     250\n";
      bool ok = false;      //后用于判断是否输出数据
      for(int i = 0; i < users.size(); i++) {
        cout << "     RCPT TO:<" << users[i] << ">\n";
        if(addr.count(users[i])) { ok = true; cout << "     250\n"; }       //检查收件人是否在用户地址里
        else cout << "     550\n";
      }
      if(ok) {      //判断是否输出数据
        cout << "     DATA\n";
        cout << "     354\n";
        cout << data;
        cout << "     .\n";
        cout << "     250\n";
      }
      cout << "     QUIT\n";
      cout << "     221\n";
    }
  }
  return 0;
}

你可能感兴趣的:(例题5-11 邮件传输代理的交互)