Coverage Report

Created: 2023-09-25 07:17

/src/neomutt/conn/accountcmd.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Connection Credentials External Command
4
 *
5
 * @authors
6
 * Copyright (C) 2022 Pietro Cerutti <gahr@gahr.ch>
7
 *
8
 * @copyright
9
 * This program is free software: you can redistribute it and/or modify it under
10
 * the terms of the GNU General Public License as published by the Free Software
11
 * Foundation, either version 2 of the License, or (at your option) any later
12
 * version.
13
 *
14
 * This program is distributed in the hope that it will be useful, but WITHOUT
15
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17
 * details.
18
 *
19
 * You should have received a copy of the GNU General Public License along with
20
 * this program.  If not, see <http://www.gnu.org/licenses/>.
21
 */
22
23
/**
24
 * @page conn_account_ext_cmd Connection Credentials External Command
25
 *
26
 * Connection Credentials External Command
27
 */
28
29
#include "config.h"
30
#include <assert.h>
31
#include <stdio.h>
32
#include <sys/types.h>
33
#include "mutt/lib.h"
34
#include "config/lib.h"
35
#include "core/lib.h"
36
#include "accountcmd.h"
37
#include "connaccount.h"
38
#include "globals.h" // IWYU pragma: keep
39
#include "mutt_account.h"
40
41
/**
42
 * make_cmd - Build the command line for the external account command.
43
 * @param buf Buffer to fill with a command line
44
 * @param cac ConnAccount to read params from
45
 * @param cmd Account command from the user config
46
 */
47
static void make_cmd(struct Buffer *buf, const struct ConnAccount *cac, const char *cmd)
48
0
{
49
0
  assert(buf && cac);
50
51
0
  buf_addstr(buf, cmd);
52
0
  buf_add_printf(buf, " --hostname %s", cac->host);
53
0
  if (cac->flags & MUTT_ACCT_USER)
54
0
  {
55
0
    buf_add_printf(buf, " --username %s", cac->user);
56
0
  }
57
58
0
  static const char *types[] = { "", "imap", "pop", "smtp", "nntp" };
59
0
  assert(sizeof(types) / sizeof(*types) == MUTT_ACCT_TYPE_MAX);
60
0
  if (cac->type != MUTT_ACCT_TYPE_NONE)
61
0
  {
62
0
    buf_add_printf(buf, " --type %s%c", types[cac->type],
63
0
                   (cac->flags & MUTT_ACCT_SSL) ? 's' : '\0');
64
0
  }
65
0
}
66
67
/**
68
 * parse_one - Parse a single line of the response
69
 * @param cac ConnAccount to write to
70
 * @param line Line from the response
71
 * @retval num #MuttAccountFlags that matched
72
 * @retval #MUTT_ACCT_NO_FLAGS Failure
73
 */
74
static MuttAccountFlags parse_one(struct ConnAccount *cac, char *line)
75
0
{
76
0
  assert(cac && line);
77
78
0
  const regmatch_t *match = mutt_prex_capture(PREX_ACCOUNT_CMD, line);
79
0
  if (!match)
80
0
  {
81
0
    mutt_perror(_("Line is malformed: expected <key: val>, got <%s>"), line);
82
0
    return MUTT_ACCT_NO_FLAGS;
83
0
  }
84
85
0
  const regmatch_t *keymatch = &match[PREX_ACCOUNT_CMD_MATCH_KEY];
86
0
  const regmatch_t *valmatch = &match[PREX_ACCOUNT_CMD_MATCH_VALUE];
87
0
  line[mutt_regmatch_start(keymatch) + mutt_regmatch_len(keymatch)] = '\0';
88
0
  const char *key = line + mutt_regmatch_start(keymatch);
89
0
  const char *val = line + mutt_regmatch_start(valmatch);
90
91
0
  switch (key[0])
92
0
  {
93
0
    case 'l':
94
0
      if (mutt_str_equal(key + 1, "ogin"))
95
0
      {
96
0
        mutt_str_copy(cac->login, val, sizeof(cac->login));
97
0
        return MUTT_ACCT_LOGIN;
98
0
      }
99
0
      break;
100
101
0
    case 'p':
102
0
      if (mutt_str_equal(key + 1, "assword"))
103
0
      {
104
0
        mutt_str_copy(cac->pass, val, sizeof(cac->pass));
105
0
        return MUTT_ACCT_PASS;
106
0
      }
107
0
      break;
108
109
0
    case 'u':
110
0
      if (mutt_str_equal(key + 1, "sername"))
111
0
      {
112
0
        mutt_str_copy(cac->user, val, sizeof(cac->user));
113
0
        return MUTT_ACCT_USER;
114
0
      }
115
0
      break;
116
117
0
    default:
118
0
      break;
119
0
  }
120
121
0
  mutt_warning(_("Unhandled key in line <%s: %s>"), key, val);
122
0
  return MUTT_ACCT_NO_FLAGS;
123
0
}
124
125
/**
126
 * call_cmd - Call the account command
127
 * @param cac ConnAccount to write to
128
 * @param cmd Command line to run
129
 * @retval num #MuttAccountFlags that matched
130
 * @retval #MUTT_ACCT_NO_FLAGS Failure
131
 */
132
static MuttAccountFlags call_cmd(struct ConnAccount *cac, const struct Buffer *cmd)
133
0
{
134
0
  assert(cac && cmd);
135
136
0
  MuttAccountFlags rc = MUTT_ACCT_NO_FLAGS;
137
138
0
  FILE *fp = NULL;
139
0
  pid_t pid = filter_create(buf_string(cmd), NULL, &fp, NULL, EnvList);
140
0
  if (pid < 0)
141
0
  {
142
0
    mutt_perror(_("Unable to run account command"));
143
0
    return rc;
144
0
  }
145
146
0
  size_t len = 0;
147
0
  char *line = NULL;
148
0
  while ((line = mutt_file_read_line(NULL, &len, fp, NULL, MUTT_RL_NO_FLAGS)))
149
0
  {
150
0
    rc |= parse_one(cac, line);
151
0
    FREE(&line);
152
0
  }
153
154
0
  mutt_file_fclose(&fp);
155
0
  filter_wait(pid);
156
0
  return rc;
157
0
}
158
159
/**
160
 * mutt_account_call_external_cmd - Retrieve account credentials via an external command
161
 * @param cac ConnAccount to fill
162
 * @retval num A bitmask of MuttAccountFlags that were retrieved on success
163
 * @retval #MUTT_ACCT_NO_FLAGS Failure
164
 */
165
MuttAccountFlags mutt_account_call_external_cmd(struct ConnAccount *cac)
166
0
{
167
0
  if (!cac || (cac->host[0] == '\0') || (cac->type == MUTT_ACCT_TYPE_NONE))
168
0
  {
169
0
    return MUTT_ACCT_NO_FLAGS;
170
0
  }
171
172
0
  const char *c_account_command = cs_subset_string(NeoMutt->sub, "account_command");
173
0
  if (!c_account_command)
174
0
  {
175
0
    return MUTT_ACCT_NO_FLAGS;
176
0
  }
177
178
0
  MuttAccountFlags rc = MUTT_ACCT_NO_FLAGS;
179
0
  struct Buffer *cmd_buf = buf_pool_get();
180
181
0
  make_cmd(cmd_buf, cac, c_account_command);
182
0
  rc = call_cmd(cac, cmd_buf);
183
0
  cac->flags |= rc;
184
185
0
  buf_pool_release(&cmd_buf);
186
0
  return rc;
187
0
}