1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
/*
* qtmaildir - a Qt6 mail client for notmuch-indexed Maildirs
* Copyright (C) 2026 Danilo M. <danix@danix.xyz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <QFileInfo>
#include <QSettings>
#include <QStandardPaths>
QString Account::scopedQuery(const QString &query) const
{
const QString prefix = QStringLiteral("path:\"%1/**\"").arg(maildir);
if (query.trimmed().isEmpty())
return prefix;
return QStringLiteral("%1 and (%2)").arg(prefix, query);
}
QString Config::defaultPath()
{
const QString base =
QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
return base + QStringLiteral("/qtmaildir/qtmaildir.conf");
}
void Config::addProblem(const QString &message)
{
m_warnings.append(message);
m_problems.append(message);
}
void Config::addNotice(const QString &message)
{
m_warnings.append(message);
}
void Config::load(const QString &path)
{
QSettings settings(path, QSettings::IniFormat);
m_notmuchConfig =
settings.value(QStringLiteral("general/notmuch_config")).toString();
m_syncCommand = settings.value(QStringLiteral("sync/command")).toString();
if (m_syncCommand.isEmpty()) {
// Not a problem: sync is optional, and nothing the user asked for is
// being ignored. A modal here would fire on every launch.
addNotice(QStringLiteral(
"No sync command configured ([sync] command); syncing is disabled."));
} else if (!QFileInfo::exists(m_syncCommand.split(QLatin1Char(' ')).first())) {
addProblem(
QStringLiteral("Sync command '%1' does not exist; syncing is disabled.")
.arg(m_syncCommand));
m_syncCommand.clear();
}
// Account groups are written as [account.work], [account.personal], etc.
// A dot, not a slash, separates the "account" namespace from the key:
// QSettings' INI backend treats "/" as its own hierarchical group
// separator, so a literal "[account/work]" section header would be
// parsed as a *nested* group "work" inside a group "account" (and trips
// a QSettings::FormatError besides), not as a single flat group named
// "account/work". "." carries no such meaning to QSettings, so
// childGroups() here returns "account.work" and "account.personal" as
// plain top-level entries and status() stays NoError.
for (const QString &group : settings.childGroups()) {
if (!group.startsWith(QStringLiteral("account.")))
continue;
Account account;
account.key = group.mid(QStringLiteral("account.").size());
settings.beginGroup(group);
account.name = settings.value(QStringLiteral("name")).toString();
account.address = settings.value(QStringLiteral("address")).toString();
account.maildir = settings.value(QStringLiteral("maildir")).toString();
account.drafts = settings.value(QStringLiteral("drafts")).toString();
// Both optional, and both describe this account's chip in the thread
// list. An account tag is a different taxonomy from a functional one,
// saying which mailbox a thread arrived in rather than what state it
// is in, so these live here rather than in [tagcolors].
account.label = settings.value(QStringLiteral("label")).toString();
const QString colour = settings.value(QStringLiteral("color")).toString();
if (!colour.isEmpty()) {
account.color = QColor(colour);
if (!account.color.isValid()) {
addProblem(
QStringLiteral("Account '%1' has an unparseable color '%2'; "
"using a generated one.")
.arg(account.key, colour));
}
}
settings.endGroup();
if (!account.isValid()) {
addProblem(
QStringLiteral("Account '%1' has no maildir; ignoring it.")
.arg(account.key));
continue;
}
m_accounts.append(account);
}
settings.beginGroup(QStringLiteral("queries"));
// QSettings::childKeys() returns keys in alphabetical order, not file
// order, so the saved-query button order in the UI is alphabetical too.
// A hand-rolled parser would be needed to preserve file order; not
// needed in v1.
for (const QString &name : settings.childKeys())
m_savedQueries.append({ name, settings.value(name).toString() });
settings.endGroup();
}
Account Config::account(const QString &key) const
{
for (const Account &a : m_accounts) {
if (a.key == key)
return a;
}
return {};
}
|