298 lines
10 KiB
C#
298 lines
10 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using Microsoft.Singularity.Smtp;
|
|
|
|
namespace Microsoft.Singularity.Smtp.Clients
|
|
{
|
|
public class SmtpClient
|
|
{
|
|
public const int _port = 25;
|
|
public static bool verbose = false;
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
|
|
private static void Usage()
|
|
{
|
|
Console.WriteLine("Usage:");
|
|
Console.WriteLine(" cli [operation] [options]");
|
|
Console.WriteLine("Options:");
|
|
Console.WriteLine(" /s:server Send to server.");
|
|
Console.WriteLine(" /? Print this help information.");
|
|
}
|
|
|
|
public static int Main(string[] args)
|
|
{
|
|
bool needHelp = false;
|
|
String client = Dns.GetHostName();
|
|
String server = "127.0.0.1";
|
|
String[] files = null;
|
|
|
|
if (args.Length == 0) {
|
|
needHelp = true;
|
|
}
|
|
|
|
Console.WriteLine("Self: {0}", client);
|
|
|
|
for (int a = 0; a < args.Length && !needHelp; a++) {
|
|
String arg = args[a];
|
|
|
|
if (arg[0] == '/') {
|
|
int colon = arg.IndexOf(':');
|
|
String option;
|
|
String values;
|
|
|
|
if (colon > 1) {
|
|
option = arg.Substring(1, colon - 1).ToLower();
|
|
values = arg.Substring(colon + 1);
|
|
}
|
|
else {
|
|
option = arg.Substring(1).ToLower();
|
|
values = null;
|
|
}
|
|
|
|
switch (option[0]) {
|
|
// Commands:
|
|
case '?': // Help
|
|
needHelp = true;
|
|
break;
|
|
|
|
case 's': // Server
|
|
case 'S':
|
|
server = values;
|
|
break;
|
|
|
|
case 'v': // Verbose
|
|
verbose = true;
|
|
break;
|
|
|
|
default:
|
|
Console.WriteLine("Unknown option: {0}", arg);
|
|
needHelp = true;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
String file = Path.GetFileName(arg);
|
|
String dir = Path.GetDirectoryName(arg);
|
|
String path = (dir == String.Empty) ?
|
|
Directory.GetCurrentDirectory() :
|
|
Path.GetFullPath(dir);
|
|
files = Directory.GetFiles(path, file);
|
|
}
|
|
}
|
|
|
|
if (files == null) {
|
|
needHelp = true;
|
|
}
|
|
|
|
if (needHelp) {
|
|
Usage();
|
|
return 1;
|
|
}
|
|
|
|
// Send the file.
|
|
|
|
String line;
|
|
Socket socket = new Socket(AddressFamily.InterNetwork,
|
|
SocketType.Stream,
|
|
ProtocolType.Tcp);
|
|
try {
|
|
IPAddress addr = IPAddress.Parse(server);
|
|
socket.Connect(new IPEndPoint(addr, _port));
|
|
} catch (SocketException e) {
|
|
Console.WriteLine("Socket connect failure: {0}", e.Message);
|
|
return 2;
|
|
}
|
|
|
|
Session session = new Session(socket);
|
|
|
|
session.Verbose = verbose;
|
|
|
|
try {
|
|
// Establish SMPT connection.
|
|
line = session.ReadLine7();
|
|
if (session.Result != 220) {
|
|
Console.WriteLine(":: Aborting due to {0}", session.Result);
|
|
return session.Quit();
|
|
}
|
|
|
|
session.WriteLine7("EHLO ", client);
|
|
|
|
for (;;) {
|
|
line = session.ReadLine7();
|
|
if (session.Result != 250) {
|
|
Console.WriteLine(":: Aborting due to {0}", session.Result);
|
|
return session.Quit();
|
|
}
|
|
|
|
if (line[3] != '-') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Send files.
|
|
|
|
foreach (string file in files) {
|
|
Console.WriteLine(":: {0}", file);
|
|
String[] lines = File.ReadAllLines(file);
|
|
|
|
#if false
|
|
// Sanitize the data.
|
|
for (int i = 0; i < lines.Length; i++) {
|
|
String data = lines[i];
|
|
|
|
if (data.Length >= 2 &&
|
|
data[data.Length - 2] == '\r' &&
|
|
data[data.Length - 1] == '\n') {
|
|
|
|
lines[i] = data.Substring(0, data.Length - 2);
|
|
}
|
|
else if (data.Length >= 1 &&
|
|
data[data.Length - 1] == '\n') {
|
|
|
|
lines[i] = data.Substring(0, data.Length - 1);
|
|
}
|
|
else if (data.Length >= 1 &&
|
|
data[data.Length - 1] == '\r') {
|
|
|
|
lines[i] = data.Substring(0, data.Length - 1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
session.WriteLine7("NOOP ", file);
|
|
line = session.ReadLine7();
|
|
if (session.Result != 250) {
|
|
Console.WriteLine(":: Aborting due to {0}", session.Result);
|
|
return session.Quit();
|
|
}
|
|
|
|
string from = FindFrom(lines).ToLower();
|
|
session.WriteLine7("MAIL FROM: <", from, ">");
|
|
line = session.ReadLine7();
|
|
if (session.Result != 250) {
|
|
Console.WriteLine(":: Aborting due to {0}", session.Result);
|
|
return session.Quit();
|
|
}
|
|
|
|
string to = FindTo("To: ", lines);
|
|
string cc = FindTo("Cc: ", lines);
|
|
string bcc = FindTo("Bcc: ", lines);
|
|
if (to == null) {
|
|
to = cc;
|
|
cc = null;
|
|
}
|
|
if (to == null) {
|
|
to = bcc;
|
|
bcc = null;
|
|
}
|
|
|
|
if (cc != null) {
|
|
to = to + "," + cc;
|
|
}
|
|
if (bcc != null) {
|
|
to = to + "," + bcc;
|
|
}
|
|
|
|
if (to == null || to.Length == 0) {
|
|
Console.WriteLine(":: Couldn't find receipients.");
|
|
return session.Quit();
|
|
}
|
|
|
|
string[] rcpts = to.Split(new Char[]{','});
|
|
for (int i = 0; i < rcpts.Length; i++) {
|
|
rcpts[i] = rcpts[i].Trim().ToLower();
|
|
if (rcpts[i].Length == 0) {
|
|
rcpts[i] = null;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < rcpts.Length; i++) {
|
|
for (int j = i + 1; j < rcpts.Length; j++) {
|
|
if (rcpts[i] == rcpts[j]) {
|
|
rcpts[j] = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (string rcpt in rcpts) {
|
|
if (rcpt != null) {
|
|
session.WriteLine7("RCPT TO: <", rcpt, ">");
|
|
line = session.ReadLine7();
|
|
if (session.Result != 250) {
|
|
Console.WriteLine(":: Aborting due to {0}", session.Result);
|
|
return session.Quit();
|
|
}
|
|
}
|
|
}
|
|
|
|
session.WriteLine7("DATA");
|
|
line = session.ReadLine7();
|
|
if (session.Result != 354) {
|
|
Console.WriteLine(":: Aborting due to {0}", session.Result);
|
|
return session.Quit();
|
|
}
|
|
|
|
foreach (string data in lines) {
|
|
if (data.Length > 0 && data[0] == '.') {
|
|
session.WritePrefix((byte)'.');
|
|
}
|
|
session.WriteLine7(data);
|
|
}
|
|
session.WriteLine7(".");
|
|
line = session.ReadLine7();
|
|
if (session.Result != 250) {
|
|
Console.WriteLine(":: Aborting due to {0}", session.Result);
|
|
return session.Quit();
|
|
}
|
|
}
|
|
session.Quit();
|
|
}
|
|
catch (SocketException e) {
|
|
Console.WriteLine("Caught socket exception: {0}", e.Message);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
public static String FindFrom(String[] lines)
|
|
{
|
|
foreach (string line in lines) {
|
|
if (line.Length == 0) {
|
|
return null; // Header ended.
|
|
}
|
|
if (line.StartsWith("From: ")) {
|
|
return line.Substring(6);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static string FindTo(String prefix, String[] lines)
|
|
{
|
|
for (int i = 0; i < lines.Length; i++) {
|
|
if (lines[i].Length == 0) {
|
|
return null; // Header ended.
|
|
}
|
|
if (lines[i].StartsWith(prefix)) {
|
|
string to = lines[i++].Substring(prefix.Length);
|
|
while (lines[i].Length > 0 && (lines[i][0] == ' ' || lines[i][0] == '\t')) {
|
|
to = to + lines[i++];
|
|
}
|
|
return to;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|