4

我正在尝试在 shell 脚本中建立与 IMAP 服务器的连接。虽然我可以连接并发出命令,但适当地关闭连接似乎是不可能的。

这是我正在使用的测试命令:

openssl s_client -crlf -connect server:993 <<EOF
01 login USERNAME PASSWORD
02 LIST "" "*"
03 logout
EOF

因为一旦输入用完连接就会关闭,这发生在接收到任何输出之前,所以我永远不会收到所需的数据。

如果我添加选项-ign_eof选项,以便它将忽略输入结束以保持连接打开,则根据需要返回输出。但是在连接关闭之后......

* BYE Logging out
03 OK Logout completed.

...s_client 保持活动状态,因此执行永远不会返回到脚本。

是否有解决方案会导致 s_client 在服务器关闭连接时终止?

或者是否有使用标准工具的替代方法?该脚本将在 Mac OS X、Debian 和 Redhat 衍生版本以及可能的 Android 终端仿真器上运行,因此我想使用相当标准的工具来实现可移植性,而不是使用专门的软件包。

更新:我想出了一个我并不完全满意的答案,但它确实有效。它使用一个脚本将命令传送到 openssl,但随后进入一个无限循环以保持标准输入打开,直到它接收到一个告诉它退出的信号。这是我的测试脚本:

#!/bin/sh

if [ "$1" = "doit" ]; then
   trap "echo \"04 logout\"; exit" SIGUSR1
   echo "00 login USERNAME PASSWORD"
   echo "01 SELECT PID-$$"
   echo "02 SELECT FOLDER"
   echo "03 FETCH 1:* (BODY[HEADER.FIELDS (Subject)])"
   while :; do :; done
fi

MYFLAG=
$0 doit | openssl s_client -crlf -connect server:993 2>/dev/null | while read LINE; do
   LINE=`echo "$LINE" | tr -d '\r'`
   [ "${LINE:0:4}" = "* OK" ] && MYFLAG=Y 
   [ "${LINE:0:33}" = "01 NO Mailbox doesn't exist: PID-" ] && PID="${LINE##*PID-}"
   [ "$MYFLAG" ] && echo "$LINE"
   [ "$PID" -a "$LINE" = "03 OK Fetch completed." ] && kill -USR1 $PID
done
echo "Finished."

该脚本使用参数调用自身以输出通过管道传输到 openssl 的 IMAP 命令,openssl 的输出通过管道传输到read循环中,以便对其进行处理。MYFLAG 变量只是用来隐藏 openssl 输出的信息,只是回显服务器连接的输出。

我选择了一个虚拟文件夹名称,其中包含脚本的第二个实例的 PID 作为将其传递回来的一种方式,显然临时文件会更好,但对于测试,我想保持一切自包含并能够观看什么正在发生。

一旦显示获取信息并且服务器返回 OK 响应,它会向脚本的第二个实例发送一个 SIGUSR1 信号,然后发送注销消息并退出,关闭 stdin 导致 s_client 断开连接。

最初我将04 logout命令包含在初始的一组回显中,但是当我这样做时,读取循环只显示到 fetch 输出然后停止,它甚至没有显示操作的 OK 状态,尽管所有内容都已收到。

此外,我需要使用tr去除尾随回车符,但如果我通过管道输出 openssl 的输出,则读取循环不会收到任何内容。

4

1 回答 1

6

您可以通过将输入定时到 openssl 来解决这个问题。我能想到的最简单的方法是使用一个将输入回显到标准输出的函数:

#!/bin/sh

imapscript () {
echo '01 login USER PASSWD'
echo '02 LIST "" "*"'
echo '03 logout'
while sleep 1; do
  echo "04 logout"
done
}

imapscript | openssl s_client -crlf -connect server:993
于 2013-04-24T16:00:14.173 回答