NeoMutt  2025-12-11-435-g4ac674
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
browse.c
Go to the documentation of this file.
1
28
34
35#include "config.h"
36#include <limits.h>
37#include <stdbool.h>
38#include <stdio.h>
39#include <string.h>
40#include "private.h"
41#include "mutt/lib.h"
42#include "config/lib.h"
43#include "email/lib.h"
44#include "core/lib.h"
45#include "conn/lib.h"
46#include "gui/lib.h"
47#include "lib.h"
48#include "browser/lib.h"
49#include "editor/lib.h"
50#include "history/lib.h"
51#include "adata.h"
52#include "mdata.h"
53#include "mutt_logging.h"
54#include "muttlib.h"
55
68static void add_folder(char delim, char *folder, bool noselect, bool noinferiors,
69 struct BrowserState *state, bool isparent)
70{
71 char tmp[PATH_MAX] = { 0 };
72 char relpath[PATH_MAX] = { 0 };
73 struct ConnAccount cac = { { 0 } };
74 char mailbox[1024] = { 0 };
75 struct FolderFile ff = { 0 };
76
77 if (imap_parse_path(state->folder, &cac, mailbox, sizeof(mailbox)))
78 return;
79
80 if (isparent)
81 {
82 /* render superiors as unix-standard ".." */
83 mutt_str_copy(relpath, "../", sizeof(relpath));
84 }
85 else if (mutt_str_startswith(folder, mailbox))
86 {
87 /* strip current folder from target, to render a relative path */
88 mutt_str_copy(relpath, folder + mutt_str_len(mailbox), sizeof(relpath));
89 }
90 else
91 {
92 mutt_str_copy(relpath, folder, sizeof(relpath));
93 }
94
95 /* apply filemask filter. This should really be done at menu setup rather
96 * than at scan, since it's so expensive to scan. But that's big changes
97 * to browser.c */
98 const struct Regex *c_mask = cs_subset_regex(NeoMutt->sub, "mask");
99 if (!mutt_regex_match(c_mask, relpath))
100 {
101 return;
102 }
103
104 imap_qualify_path(tmp, sizeof(tmp), &cac, folder);
105 ff.name = mutt_str_dup(tmp);
106
107 /* mark desc with delim in browser if it can have subfolders */
108 if (!isparent && !noinferiors && (strlen(relpath) < sizeof(relpath) - 1))
109 {
110 relpath[strlen(relpath) + 1] = '\0';
111 relpath[strlen(relpath)] = delim;
112 }
113
114 ff.desc = mutt_str_dup(relpath);
115 ff.imap = true;
116
117 /* delimiter at the root is useless. */
118 if (folder[0] == '\0')
119 delim = '\0';
120 ff.delim = delim;
121 ff.selectable = !noselect;
122 ff.inferiors = !noinferiors;
123
124 struct MailboxArray ma = neomutt_mailboxes_get(NeoMutt, MUTT_MAILBOX_ANY);
125 struct Mailbox **mp = NULL;
126 ARRAY_FOREACH(mp, &ma)
127 {
128 struct Mailbox *m = *mp;
129
130 if (mutt_str_equal(tmp, mailbox_path(m)))
131 {
132 ff.has_mailbox = true;
133 ff.has_new_mail = m->has_new;
134 ff.msg_count = m->msg_count;
135 ff.msg_unread = m->msg_unread;
136 break;
137 }
138 }
139
140 ARRAY_FREE(&ma); // Clean up the ARRAY, but not the Mailboxes
141
142 ARRAY_ADD(&state->entry, ff);
143}
144
154static int browse_add_list_result(struct ImapAccountData *adata, const char *cmd,
155 struct BrowserState *bstate, bool isparent)
156{
157 struct ImapList list = { 0 };
158 int rc;
159 struct Url *url = url_parse(bstate->folder);
160 if (!url)
161 return -1;
162
163 imap_cmd_start(adata, cmd);
164 adata->cmdresult = &list;
165 do
166 {
167 list.name = NULL;
168 rc = imap_cmd_step(adata);
169
170 if ((rc == IMAP_RES_CONTINUE) && list.name)
171 {
172 /* Let a parent folder never be selectable for navigation */
173 if (isparent)
174 list.noselect = true;
175 /* prune current folder from output */
176 if (isparent || !mutt_str_startswith(url->path, list.name))
177 add_folder(list.delim, list.name, list.noselect, list.noinferiors, bstate, isparent);
178 }
179 } while (rc == IMAP_RES_CONTINUE);
180 adata->cmdresult = NULL;
181
182 url_free(&url);
183
184 return (rc == IMAP_RES_OK) ? 0 : -1;
185}
186
196int imap_browse(const char *path, struct BrowserState *state)
197{
198 struct ImapAccountData *adata = NULL;
199 struct ImapList list = { 0 };
200 struct ConnAccount cac = { { 0 } };
201 char buf[PATH_MAX + 16];
202 char mbox[PATH_MAX] = { 0 };
203 char munged_mbox[PATH_MAX] = { 0 };
204 const char *list_cmd = NULL;
205 int len;
206 int n;
207 bool showparents = false;
208 int rc = -1;
209
210 if (imap_parse_path(path, &cac, buf, sizeof(buf)))
211 {
212 mutt_error(_("%s is an invalid IMAP path"), path);
213 return -1;
214 }
215
216 const bool c_imap_check_subscribed = cs_subset_bool(NeoMutt->sub, "imap_check_subscribed");
217 cs_subset_str_native_set(NeoMutt->sub, "imap_check_subscribed", false, NULL);
218
219 // Pick first mailbox connected to the same server
220 struct MailboxArray ma = neomutt_mailboxes_get(NeoMutt, MUTT_IMAP);
221 struct Mailbox **mp = NULL;
222 ARRAY_FOREACH(mp, &ma)
223 {
224 adata = imap_adata_get(*mp);
225 // Pick first mailbox connected on the same server
226 if (imap_account_match(&adata->conn->account, &cac))
227 break;
228 adata = NULL;
229 }
230 ARRAY_FREE(&ma); // Clean up the ARRAY, but not the Mailboxes
231
232 if (!adata)
233 goto fail;
234
235 const bool c_imap_list_subscribed = cs_subset_bool(NeoMutt->sub, "imap_list_subscribed");
236 if (c_imap_list_subscribed)
237 {
238 /* RFC3348 section 3 states LSUB is unreliable for hierarchy information.
239 * The newer LIST extensions are designed for this. */
241 list_cmd = "LIST (SUBSCRIBED RECURSIVEMATCH)";
242 else
243 list_cmd = "LSUB";
244 }
245 else
246 {
247 list_cmd = "LIST";
248 }
249
250 mutt_message(_("Getting folder list..."));
251
252 /* skip check for parents when at the root */
253 if (buf[0] == '\0')
254 {
255 mbox[0] = '\0';
256 n = 0;
257 }
258 else
259 {
260 imap_fix_path_with_delim(adata->delim, buf, mbox, sizeof(mbox));
261 n = mutt_str_len(mbox);
262 }
263
264 if (n > 0)
265 {
266 int rc_step;
267 mutt_debug(LL_DEBUG3, "mbox: %s\n", mbox);
268
269 /* if our target exists and has inferiors, enter it if we
270 * aren't already going to */
271 imap_munge_mbox_name(adata->unicode, munged_mbox, sizeof(munged_mbox), mbox);
272 len = snprintf(buf, sizeof(buf), "%s \"\" %s", list_cmd, munged_mbox);
274 snprintf(buf + len, sizeof(buf) - len, " RETURN (CHILDREN)");
275 imap_cmd_start(adata, buf);
276 adata->cmdresult = &list;
277 do
278 {
279 list.name = 0;
280 rc_step = imap_cmd_step(adata);
281 if ((rc_step == IMAP_RES_CONTINUE) && list.name)
282 {
283 if (!list.noinferiors && list.name[0] &&
284 (imap_mxcmp(list.name, mbox) == 0) && (n < sizeof(mbox) - 1))
285 {
286 mbox[n++] = list.delim;
287 mbox[n] = '\0';
288 }
289 }
290 } while (rc_step == IMAP_RES_CONTINUE);
291 adata->cmdresult = NULL;
292
293 /* if we're descending a folder, mark it as current in browser_state */
294 if (mbox[n - 1] == list.delim)
295 {
296 showparents = true;
297 imap_qualify_path(buf, sizeof(buf), &cac, mbox);
298 state->folder = mutt_str_dup(buf);
299 n--;
300 }
301
302 /* Find superiors to list
303 * Note: UW-IMAP servers return folder + delimiter when asked to list
304 * folder + delimiter. Cyrus servers don't. So we ask for folder,
305 * and tack on delimiter ourselves.
306 * Further note: UW-IMAP servers return nothing when asked for
307 * NAMESPACES without delimiters at the end. Argh! */
308 for (n--; n >= 0 && mbox[n] != list.delim; n--)
309 ; // do nothing
310
311 if (n > 0) /* "aaaa/bbbb/" -> "aaaa" */
312 {
313 /* forget the check, it is too delicate (see above). Have we ever
314 * had the parent not exist? */
315 char ctmp = mbox[n];
316 mbox[n] = '\0';
317
318 if (showparents)
319 {
320 mutt_debug(LL_DEBUG3, "adding parent %s\n", mbox);
321 add_folder(list.delim, mbox, true, false, state, true);
322 }
323
324 /* if our target isn't a folder, we are in our superior */
325 if (!state->folder)
326 {
327 /* store folder with delimiter */
328 mbox[n++] = ctmp;
329 ctmp = mbox[n];
330 mbox[n] = '\0';
331 imap_qualify_path(buf, sizeof(buf), &cac, mbox);
332 state->folder = mutt_str_dup(buf);
333 }
334 mbox[n] = ctmp;
335 }
336 else
337 {
338 /* "/bbbb/" -> add "/", "aaaa/" -> add "" */
339 char relpath[2] = { 0 };
340 /* folder may be "/" */
341 snprintf(relpath, sizeof(relpath), "%c", (n < 0) ? '\0' : adata->delim);
342 if (showparents)
343 add_folder(adata->delim, relpath, true, false, state, true);
344 if (!state->folder)
345 {
346 imap_qualify_path(buf, sizeof(buf), &cac, relpath);
347 state->folder = mutt_str_dup(buf);
348 }
349 }
350 }
351
352 /* no namespace, no folder: set folder to host only */
353 if (!state->folder)
354 {
355 imap_qualify_path(buf, sizeof(buf), &cac, NULL);
356 state->folder = mutt_str_dup(buf);
357 }
358
359 mutt_debug(LL_DEBUG3, "Quoting mailbox scan: %s -> ", mbox);
360 snprintf(buf, sizeof(buf), "%s%%", mbox);
361 imap_munge_mbox_name(adata->unicode, munged_mbox, sizeof(munged_mbox), buf);
362 mutt_debug(LL_DEBUG3, "%s\n", munged_mbox);
363 len = snprintf(buf, sizeof(buf), "%s \"\" %s", list_cmd, munged_mbox);
365 snprintf(buf + len, sizeof(buf) - len, " RETURN (CHILDREN)");
366 if (browse_add_list_result(adata, buf, state, false))
367 goto fail;
368
369 if (ARRAY_EMPTY(&state->entry))
370 {
371 // L10N: (%s) is the name / path of the folder we were trying to browse
372 mutt_error(_("No such folder: %s"), path);
373 goto fail;
374 }
375
377 rc = 0;
378
379fail:
380 cs_subset_str_native_set(NeoMutt->sub, "imap_check_subscribed",
381 c_imap_check_subscribed, NULL);
382 return rc;
383}
384
393int imap_mailbox_create(const char *path)
394{
395 struct ImapAccountData *adata = NULL;
396 struct ImapMboxData *mdata = NULL;
397 struct Buffer *name = buf_pool_get();
398 int rc = -1;
399
400 if (imap_adata_find(path, &adata, &mdata) < 0)
401 {
402 mutt_debug(LL_DEBUG1, "Couldn't find open connection to %s\n", path);
403 goto done;
404 }
405
406 /* append a delimiter if necessary */
407 const size_t n = buf_strcpy(name, mdata->real_name);
408 if ((n != 0) && (buf_at(name, n - 1) != adata->delim))
409 {
410 buf_addch(name, adata->delim);
411 }
412
413 struct FileCompletionData cdata = { false, NULL, NULL, NULL, NULL };
414 if (mw_get_field(_("Create mailbox: "), name, MUTT_COMP_NO_FLAGS, HC_MAILBOX,
415 &CompleteMailboxOps, &cdata) != 0)
416 {
417 goto done;
418 }
419
420 if (buf_is_empty(name))
421 {
422 mutt_error(_("Mailbox must have a name"));
423 goto done;
424 }
425
426 if (imap_create_mailbox(adata, buf_string(name)) < 0)
427 goto done;
428
429 imap_mdata_free((void *) &mdata);
430 mutt_message(_("Mailbox created"));
431 mutt_sleep(0);
432 rc = 0;
433
434done:
435 imap_mdata_free((void *) &mdata);
436 buf_pool_release(&name);
437 return rc;
438}
439
448int imap_mailbox_rename(const char *path)
449{
450 struct ImapAccountData *adata = NULL;
451 struct ImapMboxData *mdata = NULL;
452 struct Buffer *buf = NULL;
453 struct Buffer *newname = NULL;
454 int rc = -1;
455
456 if (imap_adata_find(path, &adata, &mdata) < 0)
457 {
458 mutt_debug(LL_DEBUG1, "Couldn't find open connection to %s\n", path);
459 goto done;
460 }
461
462 if (mdata->real_name[0] == '\0')
463 {
464 mutt_error(_("Can't rename root folder"));
465 goto done;
466 }
467
468 buf = buf_pool_get();
469 newname = buf_pool_get();
470
471 buf_printf(buf, _("Rename mailbox %s to: "), mdata->name);
472 buf_strcpy(newname, mdata->name);
473
474 struct FileCompletionData cdata = { false, NULL, NULL, NULL, NULL };
476 &CompleteMailboxOps, &cdata) != 0)
477 {
478 goto done;
479 }
480
481 if (buf_is_empty(newname))
482 {
483 mutt_error(_("Mailbox must have a name"));
484 goto done;
485 }
486
487 imap_fix_path_with_delim(adata->delim, buf_string(newname), buf->data, buf->dsize);
488
489 if (imap_rename_mailbox(adata, mdata->name, buf_string(buf)) < 0)
490 {
491 mutt_error(_("Rename failed: %s"), imap_get_qualifier(adata->buf));
492 goto done;
493 }
494
495 mutt_message(_("Mailbox renamed"));
496 mutt_sleep(0);
497 rc = 0;
498
499done:
500 imap_mdata_free((void *) &mdata);
501 buf_pool_release(&buf);
502 buf_pool_release(&newname);
503
504 return rc;
505}
#define ARRAY_ADD(head, elem)
Add an element at the end of the array.
Definition array.h:157
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition array.h:223
#define ARRAY_EMPTY(head)
Check if an array is empty.
Definition array.h:74
#define ARRAY_FREE(head)
Release all memory.
Definition array.h:209
int imap_browse(const char *path, struct BrowserState *state)
IMAP hook into the folder browser.
Definition browse.c:196
static int browse_add_list_result(struct ImapAccountData *adata, const char *cmd, struct BrowserState *bstate, bool isparent)
Add entries to the folder browser.
Definition browse.c:154
static void add_folder(char delim, char *folder, bool noselect, bool noinferiors, struct BrowserState *state, bool isparent)
Format and add an IMAP folder to the browser.
Definition browse.c:68
int imap_mailbox_create(const char *path)
Create a new IMAP mailbox.
Definition browse.c:393
int imap_mailbox_rename(const char *path)
Rename a mailbox.
Definition browse.c:448
const struct CompleteOps CompleteMailboxOps
Auto-Completion of Files / Mailboxes.
Definition complete.c:159
Select a Mailbox from a list.
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition buffer.c:161
bool buf_is_empty(const struct Buffer *buf)
Is the Buffer empty?
Definition buffer.c:291
char buf_at(const struct Buffer *buf, size_t offset)
Return the character at the given offset.
Definition buffer.c:668
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition buffer.c:241
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition buffer.h:96
const struct Regex * cs_subset_regex(const struct ConfigSubset *sub, const char *name)
Get a regex config item by name.
Definition helpers.c:217
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
Convenience wrapper for the config headers.
Connection Library.
Convenience wrapper for the core headers.
static const char * mailbox_path(const struct Mailbox *m)
Get the Mailbox's path string.
Definition mailbox.h:213
@ MUTT_IMAP
'IMAP' Mailbox type
Definition mailbox.h:49
@ MUTT_MAILBOX_ANY
Match any Mailbox type.
Definition mailbox.h:41
Edit a string.
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition wdata.h:42
Structs that make up an email.
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition window.c:270
#define mutt_error(...)
Definition logging2.h:94
#define mutt_message(...)
Definition logging2.h:93
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
void imap_mdata_free(void **ptr)
Free the private Mailbox data - Implements Mailbox::mdata_free() -.
Definition mdata.c:40
Convenience wrapper for the gui headers.
Read/write command history from/to a file.
@ HC_MAILBOX
Mailboxes.
Definition lib.h:61
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition adata.c:123
Imap-specific Account data.
int imap_cmd_start(struct ImapAccountData *adata, const char *cmdstr)
Given an IMAP command, send it to the server.
Definition command.c:1196
int imap_cmd_step(struct ImapAccountData *adata)
Reads server responses from an IMAP command.
Definition command.c:1210
IMAP network mailbox.
int imap_parse_path(const char *path, struct ConnAccount *cac, char *mailbox, size_t mailboxlen)
Parse an IMAP mailbox name into ConnAccount, name.
Definition util.c:478
int imap_mxcmp(const char *mx1, const char *mx2)
Compare mailbox names, giving priority to INBOX.
Definition util.c:549
Imap-specific Mailbox data.
Shared constants/structs that are private to IMAP.
void imap_qualify_path(char *buf, size_t buflen, struct ConnAccount *conn_account, char *path)
Make an absolute IMAP folder target.
Definition util.c:856
#define IMAP_RES_OK
<tag> OK ...
Definition private.h:54
char * imap_fix_path_with_delim(char delim, const char *mailbox, char *path, size_t plen)
Fix up the imap path.
Definition util.c:714
#define IMAP_CAP_LIST_EXTENDED
RFC5258: IMAP4 LIST Command Extensions.
Definition private.h:138
int imap_adata_find(const char *path, struct ImapAccountData **adata, struct ImapMboxData **mdata)
Find the Account data for this path.
Definition util.c:72
bool imap_account_match(const struct ConnAccount *a1, const struct ConnAccount *a2)
Compare two Accounts.
Definition util.c:1096
void imap_munge_mbox_name(bool unicode, char *dest, size_t dlen, const char *src)
Quote awkward characters in a mailbox name.
Definition util.c:961
#define IMAP_RES_CONTINUE
* ...
Definition private.h:55
char * imap_get_qualifier(char *buf)
Get the qualifier from a tagged response.
Definition util.c:808
int imap_rename_mailbox(struct ImapAccountData *adata, char *oldname, const char *newname)
Rename a mailbox.
Definition imap.c:584
int imap_create_mailbox(struct ImapAccountData *adata, const char *mailbox)
Create a new mailbox.
Definition imap.c:542
@ LL_DEBUG3
Log at debug level 3.
Definition logging2.h:47
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
Convenience wrapper for the library headers.
#define _(a)
Definition message.h:28
bool mutt_regex_match(const struct Regex *regex, const char *str)
Shorthand to mutt_regex_capture()
Definition regex.c:614
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:662
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition string.c:234
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:500
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition string.c:583
#define PATH_MAX
Definition mutt.h:49
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
NeoMutt Logging.
void mutt_sleep(short s)
Sleep for a while.
Definition muttlib.c:786
Some miscellaneous functions.
struct MailboxArray neomutt_mailboxes_get(struct NeoMutt *n, enum MailboxType type)
Get an Array of matching Mailboxes.
Definition neomutt.c:526
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition pool.c:91
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition pool.c:111
void * adata
Private data (for Mailbox backends)
Definition account.h:42
State of the file/mailbox browser.
Definition lib.h:145
char * folder
Folder name.
Definition lib.h:148
struct BrowserEntryArray entry
Array of files / dirs / mailboxes.
Definition lib.h:146
String manipulation buffer.
Definition buffer.h:36
size_t dsize
Length of data.
Definition buffer.h:39
char * data
Pointer to data.
Definition buffer.h:37
Login details for a remote server.
Definition connaccount.h:53
struct ConnAccount account
Account details: username, password, etc.
Definition connection.h:49
Input for the file completion function.
Definition curs_lib.h:39
Browser entry representing a folder/dir.
Definition lib.h:79
bool selectable
Folder can be selected.
Definition lib.h:97
char delim
Path delimiter.
Definition lib.h:94
bool imap
This is an IMAP folder.
Definition lib.h:96
bool has_mailbox
This is a mailbox.
Definition lib.h:99
char * name
Name of file/dir/mailbox.
Definition lib.h:87
bool has_new_mail
true if mailbox has "new mail"
Definition lib.h:90
char * desc
Description of mailbox.
Definition lib.h:88
int msg_count
total number of messages
Definition lib.h:91
bool inferiors
Folder has children.
Definition lib.h:98
int msg_unread
number of unread messages
Definition lib.h:92
IMAP-specific Account data -.
Definition adata.h:40
char delim
Path delimiter.
Definition adata.h:78
struct ImapList * cmdresult
Resuls of complicated commands.
Definition adata.h:69
bool unicode
If true, we can send UTF-8, and the server will use UTF8 rather than mUTF7.
Definition adata.h:63
ImapCapFlags capabilities
Capability flags.
Definition adata.h:55
char * buf
Command buffer.
Definition adata.h:60
struct Connection * conn
Connection to IMAP server.
Definition adata.h:41
Items in an IMAP browser.
Definition private.h:149
bool noselect
Mailbox is not selectable.
Definition private.h:152
bool noinferiors
Mailbox has no children.
Definition private.h:153
char * name
Mailbox name.
Definition private.h:150
char delim
Hierarchy delimiter.
Definition private.h:151
IMAP-specific Mailbox data -.
Definition mdata.h:40
char * real_name
Original Mailbox name, e.g.: INBOX can be just \0.
Definition mdata.h:43
char * name
Mailbox name.
Definition mdata.h:41
A mailbox.
Definition mailbox.h:78
bool has_new
Mailbox has new mail.
Definition mailbox.h:84
int msg_count
Total number of messages.
Definition mailbox.h:87
void * mdata
Driver specific data.
Definition mailbox.h:131
int msg_unread
Number of unread messages.
Definition mailbox.h:88
Container for Accounts, Notifications.
Definition neomutt.h:41
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Cached regular expression.
Definition regex3.h:85
A parsed URL proto://user:password@host:port/path?a=1&b=2
Definition url.h:69
char * path
Path.
Definition url.h:75
int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name, intptr_t value, struct Buffer *err)
Natively set the value of a string config item.
Definition subset.c:303
struct Url * url_parse(const char *src)
Fill in Url.
Definition url.c:239
void url_free(struct Url **ptr)
Free the contents of a URL.
Definition url.c:124