如何从 gjs gtk 应用程序发送电子邮件(生成邮件)
how to send email (spawn mail) from gjs gtk app
我正在尝试编写一个需要发送电子邮件的 gjs 应用程序。
我发现这样做的方法是使用 spawn_async_with_pipes() 来调用邮件。
该应用程序似乎生成邮件,我没有收到错误,但我没有收到任何有用的输出,也没有收到测试电子邮件...
我从事此工作已有一段时间了,但几乎没有发现有用的最新文档。我正在使用 gtk3 和 gjs(和 glib)。我还尝试生成一个 shell 脚本,该脚本又调用邮件。这导致 "could not resolve host" 错误和死信队列。所以我知道我正在产生我的命令。我不关心 "could not resolve host command",但事实上我无法通过直接生成邮件来获取它。
我是这样生成邮件的:
const [res, pid, in_fd, out_fd, err_fd] =
await GLib.spawn_async_with_pipes(null,
['mail',
'-V',
`-s "${msgObj.subBlock}"`,
`-r ${to}`,
`-S smtp=${HOST}`,
'-S smtp-use-starttls',
'-S smtp-auth=login',
`-S smtp-auth-user=${USER}`,
`-S smtp-auth-password=${PASS}`,
FROM
], null, GLib.SpawnFlags.SEARCH_PATH, null);
const in_reader = new Gio.DataOutputStream({
base_stream: new Gio.UnixOutputStream({fd: in_fd})
});
var feedRes = in_reader.put_string(msgObj.msgBlock, null);
const out_reader = new Gio.DataInputStream({
base_stream: new Gio.UnixInputStream({fd: out_fd})
});
const err_reader = new Gio.DataInputStream({
base_stream: new Gio.UnixInputStream({fd: err_fd})
});
var out = out_reader.read_until("", null);
var err = err_reader.read_until("", null);
print(` > out : "${out}"`);
print(` > res : "${res}"`);
print(` > feedRes : "${feedRes}"`);
print(` > err : "${err}"`);
err 是 0
,而 res
只是 true
我不知道输出应该是什么,但我没有收到可识别的错误,也没有发送电子邮件...
如何让我的应用程序发送电子邮件?产生邮件不是要走的路吗?
提前感谢您给我的任何指示。
这里有几件事让你感到困惑,我想我可以解决。
await GLib.spawn_async_with_pipes(
GLib 有自己的异步函数概念,在适用时需要将其包装在 Promise 中才能有效地使用 await
关键字。在这种情况下,GLib.spawn_async_with_pipes()
并不是您所想的那样异步,但这没关系,因为我们将使用更高级别 class Gio.Subprocess
.
async function mail(msgObj, to, host, user, pass, cancellable = null) {
try {
let proc = new Gio.Subprocess({
argv: ['mail',
'-V',
// Option switches and values are separate args
'-s', `"${msgObj.subBlock}"`,
'-r', `${to}`,
'-S', `smtp=${host}`,
'-S', 'smtp-use-starttls',
'-S', 'smtp-auth=login',
'-S', `smtp-auth-user=${user}`,
'-S', `smtp-auth-password=${pass}`,
FROM
],
flags: Gio.SubprocessFlags.STDIN_PIPE |
Gio.SubprocessFlags.STDOUT_PIPE |
Gio.SubprocessFlags.STDERR_MERGE
});
// Classes that implement GInitable must be initialized before use, but
// you could use Gio.Subprocess.new(argv, flags) which will call this for you
proc.init(cancellable);
// We're going to wrap a GLib async function in a Promise so we can
// use it like a native JavaScript async function.
//
// You could alternatively return this Promise instead of awaiting it
// here, but that's up to you.
let stdout = await new Promise((resolve, reject) => {
// communicate_utf8() returns a string, communicate() returns a
// a GLib.Bytes and there are "headless" functions available as well
proc.communicate_utf8_async(
// This is your stdin, which can just be a JS string
msgObj.msgBlock,
// we've been passing this around from the function args; you can
// create a Gio.Cancellable and call `cancellable.cancel()` to
// stop the command or any other operation you've passed it to at
// any time, which will throw an "Operation Cancelled" error.
cancellable,
// This is the GAsyncReady callback, which works like any other
// callback, but we need to ensure we catch errors so we can
// propagate them with `reject()` to make the Promise work
// properly
(proc, res) => {
try {
let [ok, stdout, stderr] = proc.communicate_utf8_finish(res);
// Because we used the STDERR_MERGE flag stderr will be
// included in stdout. Obviously you could also call
// `resolve([stdout, stderr])` if you wanted to keep both
// and separate them.
//
// This won't affect whether the proc actually return non-
// zero causing the Promise to reject()
resolve(stdout);
} catch (e) {
reject(e);
}
}
);
});
return stdout;
} catch (e) {
// This could be any number of errors, but probably it will be a GError
// in which case it will have `code` property carrying a GIOErrorEnum
// you could use to programmatically respond to, if desired.
logError(e);
}
}
Gio.Subprocess
总体上是更好的选择,但对于无法将 "out" 参数传递给函数的语言绑定尤其如此。使用 GLib.spawn_async_with_pipes
您通常会传入 NULL
以防止打开任何您不想要的管道,并始终确保关闭任何您不需要的管道。由于我们不能在 GJS 中这样做,您最终可能会遇到无法关闭的悬空文件描述符。
Gio.Subprocess
为您做了很多工作,确保文件描述符正在关闭,防止僵尸进程,为您设置子手表以及其他您真正不想担心的事情。它还具有获取 IO 流的便利功能,因此您不必自己包装 fd,以及其他有用的东西。
我写了一篇较长的 GJS 异步编程入门读物,您可能会觉得有用 here。你应该能够很快地通过它,它试图消除一些关于 GLib 异步、JavaScript 异步和 GLib 主循环与 JS 事件循环之间关系的混淆。
我正在尝试编写一个需要发送电子邮件的 gjs 应用程序。 我发现这样做的方法是使用 spawn_async_with_pipes() 来调用邮件。 该应用程序似乎生成邮件,我没有收到错误,但我没有收到任何有用的输出,也没有收到测试电子邮件...
我从事此工作已有一段时间了,但几乎没有发现有用的最新文档。我正在使用 gtk3 和 gjs(和 glib)。我还尝试生成一个 shell 脚本,该脚本又调用邮件。这导致 "could not resolve host" 错误和死信队列。所以我知道我正在产生我的命令。我不关心 "could not resolve host command",但事实上我无法通过直接生成邮件来获取它。
我是这样生成邮件的:
const [res, pid, in_fd, out_fd, err_fd] =
await GLib.spawn_async_with_pipes(null,
['mail',
'-V',
`-s "${msgObj.subBlock}"`,
`-r ${to}`,
`-S smtp=${HOST}`,
'-S smtp-use-starttls',
'-S smtp-auth=login',
`-S smtp-auth-user=${USER}`,
`-S smtp-auth-password=${PASS}`,
FROM
], null, GLib.SpawnFlags.SEARCH_PATH, null);
const in_reader = new Gio.DataOutputStream({
base_stream: new Gio.UnixOutputStream({fd: in_fd})
});
var feedRes = in_reader.put_string(msgObj.msgBlock, null);
const out_reader = new Gio.DataInputStream({
base_stream: new Gio.UnixInputStream({fd: out_fd})
});
const err_reader = new Gio.DataInputStream({
base_stream: new Gio.UnixInputStream({fd: err_fd})
});
var out = out_reader.read_until("", null);
var err = err_reader.read_until("", null);
print(` > out : "${out}"`);
print(` > res : "${res}"`);
print(` > feedRes : "${feedRes}"`);
print(` > err : "${err}"`);
err 是 0
,而 res
只是 true
我不知道输出应该是什么,但我没有收到可识别的错误,也没有发送电子邮件... 如何让我的应用程序发送电子邮件?产生邮件不是要走的路吗? 提前感谢您给我的任何指示。
这里有几件事让你感到困惑,我想我可以解决。
await GLib.spawn_async_with_pipes(
GLib 有自己的异步函数概念,在适用时需要将其包装在 Promise 中才能有效地使用 await
关键字。在这种情况下,GLib.spawn_async_with_pipes()
并不是您所想的那样异步,但这没关系,因为我们将使用更高级别 class Gio.Subprocess
.
async function mail(msgObj, to, host, user, pass, cancellable = null) {
try {
let proc = new Gio.Subprocess({
argv: ['mail',
'-V',
// Option switches and values are separate args
'-s', `"${msgObj.subBlock}"`,
'-r', `${to}`,
'-S', `smtp=${host}`,
'-S', 'smtp-use-starttls',
'-S', 'smtp-auth=login',
'-S', `smtp-auth-user=${user}`,
'-S', `smtp-auth-password=${pass}`,
FROM
],
flags: Gio.SubprocessFlags.STDIN_PIPE |
Gio.SubprocessFlags.STDOUT_PIPE |
Gio.SubprocessFlags.STDERR_MERGE
});
// Classes that implement GInitable must be initialized before use, but
// you could use Gio.Subprocess.new(argv, flags) which will call this for you
proc.init(cancellable);
// We're going to wrap a GLib async function in a Promise so we can
// use it like a native JavaScript async function.
//
// You could alternatively return this Promise instead of awaiting it
// here, but that's up to you.
let stdout = await new Promise((resolve, reject) => {
// communicate_utf8() returns a string, communicate() returns a
// a GLib.Bytes and there are "headless" functions available as well
proc.communicate_utf8_async(
// This is your stdin, which can just be a JS string
msgObj.msgBlock,
// we've been passing this around from the function args; you can
// create a Gio.Cancellable and call `cancellable.cancel()` to
// stop the command or any other operation you've passed it to at
// any time, which will throw an "Operation Cancelled" error.
cancellable,
// This is the GAsyncReady callback, which works like any other
// callback, but we need to ensure we catch errors so we can
// propagate them with `reject()` to make the Promise work
// properly
(proc, res) => {
try {
let [ok, stdout, stderr] = proc.communicate_utf8_finish(res);
// Because we used the STDERR_MERGE flag stderr will be
// included in stdout. Obviously you could also call
// `resolve([stdout, stderr])` if you wanted to keep both
// and separate them.
//
// This won't affect whether the proc actually return non-
// zero causing the Promise to reject()
resolve(stdout);
} catch (e) {
reject(e);
}
}
);
});
return stdout;
} catch (e) {
// This could be any number of errors, but probably it will be a GError
// in which case it will have `code` property carrying a GIOErrorEnum
// you could use to programmatically respond to, if desired.
logError(e);
}
}
Gio.Subprocess
总体上是更好的选择,但对于无法将 "out" 参数传递给函数的语言绑定尤其如此。使用 GLib.spawn_async_with_pipes
您通常会传入 NULL
以防止打开任何您不想要的管道,并始终确保关闭任何您不需要的管道。由于我们不能在 GJS 中这样做,您最终可能会遇到无法关闭的悬空文件描述符。
Gio.Subprocess
为您做了很多工作,确保文件描述符正在关闭,防止僵尸进程,为您设置子手表以及其他您真正不想担心的事情。它还具有获取 IO 流的便利功能,因此您不必自己包装 fd,以及其他有用的东西。
我写了一篇较长的 GJS 异步编程入门读物,您可能会觉得有用 here。你应该能够很快地通过它,它试图消除一些关于 GLib 异步、JavaScript 异步和 GLib 主循环与 JS 事件循环之间关系的混淆。