как отправить письмо (spawn mail) из приложения gjs gtk - PullRequest
0 голосов
/ 25 января 2019

Я пытаюсь написать приложение для gjs, которое должно отправлять электронные письма. Я нашел способ сделать это с помощью spawn_async_with_pipes () для вызова почты. Кажется, что приложение порождает почту, и я не получаю сообщение об ошибке, но я не получаю никакой полезной информации и не получаю тестовые электронные письма ...

Я занимаюсь этим уже некоторое время и не нашел ничего полезного в обновлении документации. Я работаю с gtk3 и gjs (и glib). Я также попытался создать сценарий оболочки, который в свою очередь вызывает почту. Это приводило к ошибкам «не удалось разрешить хост» и к очереди недоставленных сообщений. Итак, я знаю, что я порождаю свою команду. Меня не беспокоит «не удалось разрешить команду хоста», но то, что я не могу получить ее, порождая почту напрямую.

Я порождаю почту вот так:

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}"`);

ошибка 0, res true

Я не знаю, каким должен быть вывод, но я не получаю распознаваемой ошибки, и электронная почта не доставляется ... Как я могу заставить мое приложение отправлять электронные письма? Разве не является порождением почты? Заранее спасибо за любые указатели, которые вы можете дать мне.

1 Ответ

0 голосов
/ 26 января 2019

Здесь есть пара вещей, которые, я думаю, сбивают вас с толку. Думаю, я могу все прояснить.

await GLib.spawn_async_with_pipes(

GLib имеет свою собственную концепцию асинхронных функций, которую при необходимости необходимо заключить в Promise для эффективной работы с ключевым словом await. В этом случае GLib.spawn_async_with_pipes() не является асинхронным, как вы думаете, но это нормально, потому что мы собираемся использовать класс более высокого уровня 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 является лучшим выбором в целом, но особенно для языковых привязок, которые не могут передавать «аргументы» в функции. Используя GLib.spawn_async_with_pipes, вы обычно вводите NULL, чтобы предотвратить открытие труб, которые вам не нужны, и всегда гарантируете закрытие труб, которые вам не нужны. Поскольку мы не можем сделать это в GJS, вы можете получить висячие файловые дескрипторы, которые вы не сможете закрыть.

Gio.Subprocess выполняет большую часть работы за вас и обеспечивает закрытие дескрипторов файлов, предотвращает процессы зомби, настраивает дочерние часы для вас и другие вещи, о которых вы действительно не хотите беспокоиться. Он также имеет удобные функции для получения потоков ввода-вывода, поэтому вам не придется самостоятельно оборачивать fd, среди других полезных вещей.

Я написал более длинный учебник по асинхронному программированию в GJS, который может оказаться полезным здесь . Вы должны быть в состоянии справиться с этим довольно быстро, и он попытается прояснить некоторую путаницу в отношениях между асинхронностью GLib, асинхронностью JavaScript и главным циклом GLib против цикла событий JS.

...