Commit c7e156e1 authored by Thomas Roessler's avatar Thomas Roessler

Fix index updates when closing or synching mail folders fails.

There were some bug-fixes hidden in that code.
parent 028d0e5a
......@@ -696,7 +696,7 @@ int mutt_save_attachment (FILE *fp, BODY *m, char *path, int flags, HEADER *hdr)
return -1;
if ((msg = mx_open_new_message (&ctx, hn, is_from (buf, NULL, 0) ? 0 : M_ADD_FROM)) == NULL)
{
mx_close_mailbox(&ctx);
mx_close_mailbox(&ctx, NULL);
return -1;
}
if (ctx.magic == M_MBOX || ctx.magic == M_MMDF || ctx.magic == M_KENDRA)
......@@ -709,7 +709,7 @@ int mutt_save_attachment (FILE *fp, BODY *m, char *path, int flags, HEADER *hdr)
r = -1;
mx_close_message (&msg);
mx_close_mailbox (&ctx);
mx_close_mailbox (&ctx, NULL);
return r;
}
else
......
......@@ -658,7 +658,7 @@ int mutt_save_message (HEADER *h, int delete,
need_buffy_cleanup = (ctx.magic == M_MBOX || ctx.magic == M_MMDF || ctx.magic == M_KENDRA);
mx_close_mailbox (&ctx);
mx_close_mailbox (&ctx, NULL);
if (need_buffy_cleanup)
{
......
......@@ -669,7 +669,7 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
if (!ctx->msgcount)
{
mx_close_mailbox (ctx);
mx_close_mailbox (ctx, NULL);
safe_free ((void **) &ctx);
mutt_error _("No messages in that folder.");
break;
......@@ -720,7 +720,7 @@ int mutt_compose_menu (HEADER *msg, /* structure for new message */
menu->redraw |= REDRAW_FULL;
if (close == OP_QUIT)
mx_close_mailbox (Context);
mx_close_mailbox (Context, NULL);
else
mx_fastclose_mailbox (Context);
safe_free ((void **) &Context);
......
......@@ -240,6 +240,113 @@ static int mx_toggle_write (CONTEXT *ctx)
return 0;
}
static void update_index (MUTTMENU *menu, CONTEXT *ctx, int check,
int oldcount, int index_hint)
{
/* store pointers to the newly added messages */
HEADER **save_new = NULL;
int j;
/* take note of the current message */
if (oldcount)
{
if (menu->current < Context->vcount)
menu->oldcurrent = index_hint;
else
oldcount = 0; /* invalid message number! */
}
/* We are in a limited view. Check if the new message(s) satisfy
* the limit criteria. If they do, set their virtual msgno so that
* they will be visible in the limited view */
if (Context->pattern)
{
#define THIS_BODY Context->hdrs[j]->content
if (oldcount || check == M_REOPENED)
{
for (j = (check == M_REOPENED) ? 0 : oldcount; j < Context->msgcount; j++)
{
if (mutt_pattern_exec (Context->limit_pattern,
M_MATCH_FULL_ADDRESS,
Context, Context->hdrs[j]))
{
Context->hdrs[j]->virtual = Context->vcount;
Context->v2r[Context->vcount] = j;
Context->hdrs[j]->limited = 1;
Context->vcount++;
Context->vsize += THIS_BODY->length + THIS_BODY->offset - THIS_BODY->hdr_offset;
}
}
}
#undef THIS_BODY
}
/* save the list of new messages */
if (oldcount && check != M_REOPENED
&& ((Sort & SORT_MASK) == SORT_THREADS))
{
save_new = (HEADER **) safe_malloc (sizeof (HEADER *) * (Context->msgcount - oldcount));
for (j = oldcount; j < Context->msgcount; j++)
save_new[j-oldcount] = Context->hdrs[j];
}
/* if the mailbox was reopened, need to rethread from scratch */
mutt_sort_headers (Context, (check == M_REOPENED));
/* uncollapse threads with new mail */
if ((Sort & SORT_MASK) == SORT_THREADS)
{
if (check == M_REOPENED)
{
HEADER *h;
h = Context->tree;
Context->collapsed = 0;
while (h)
{
mutt_uncollapse_thread (Context, h);
h = h->next;
}
mutt_set_virtual (Context);
}
else if (oldcount)
{
for (j = 0; j < Context->msgcount - oldcount; j++)
{
int k;
for (k = 0; k < Context->msgcount; k++)
{
HEADER *h = Context->hdrs[k];
if (h == save_new[j] && (!Context->pattern || h->limited))
mutt_uncollapse_thread (Context, h);
}
}
FREE (&save_new);
mutt_set_virtual (Context);
}
}
menu->current = -1;
if (oldcount)
{
/* restore the current message to the message it was pointing to */
for (j = 0; j < Context->vcount; j++)
{
if (Context->hdrs[Context->v2r[j]]->index == menu->oldcurrent)
{
menu->current = j;
break;
}
}
}
if (menu->current < 0)
menu->current = ci_first_message ();
}
static void resort_index (MUTTMENU *menu)
{
int i;
......@@ -289,7 +396,6 @@ int mutt_index_menu (void)
int do_buffy_notify = 1;
int close = 0; /* did we OP_QUIT or OP_EXIT out of this menu? */
int attach_msg = option(OPTATTACHMSG);
int check_lock = 0;
menu = mutt_new_menu ();
menu->menu = MENU_MAIN;
......@@ -331,7 +437,7 @@ int mutt_index_menu (void)
index_hint = (Context->vcount) ? CURHDR->index : 0;
if ((check = mx_check_mailbox (Context, &index_hint, check_lock)) < 0)
if ((check = mx_check_mailbox (Context, &index_hint, 0)) < 0)
{
if (!Context->path)
{
......@@ -344,103 +450,8 @@ int mutt_index_menu (void)
}
else if (check == M_NEW_MAIL || check == M_REOPENED)
{
/* store pointers to the newly added messages */
HEADER **save_new = NULL;
/* take note of the current message */
if (oldcount)
{
if (menu->current < Context->vcount)
menu->oldcurrent = index_hint;
else
oldcount = 0; /* invalid message number! */
}
/* We are in a limited view. Check if the new message(s) satisfy
* the limit criteria. If they do, set their virtual msgno so that
* they will be visible in the limited view */
if (Context->pattern)
{
#define THIS_BODY Context->hdrs[j]->content
if (oldcount || check == M_REOPENED)
for (j = (check == M_REOPENED) ? 0 : oldcount; j < Context->msgcount; j++)
{
if (mutt_pattern_exec (Context->limit_pattern,
M_MATCH_FULL_ADDRESS,
Context, Context->hdrs[j]))
{
Context->hdrs[j]->virtual = Context->vcount;
Context->v2r[Context->vcount] = j;
Context->hdrs[j]->limited = 1;
Context->vcount++;
Context->vsize += THIS_BODY->length + THIS_BODY->offset - THIS_BODY->hdr_offset;
}
}
#undef THIS_BODY
}
/* save the list of new messages */
if (oldcount && check != M_REOPENED
&& ((Sort & SORT_MASK) == SORT_THREADS))
{
save_new = (HEADER **) safe_malloc (sizeof (HEADER *) * (Context->msgcount - oldcount));
for (j = oldcount; j < Context->msgcount; j++)
save_new[j-oldcount] = Context->hdrs[j];
}
/* if the mailbox was reopened, need to rethread from scratch */
mutt_sort_headers (Context, (check == M_REOPENED));
/* uncollapse threads with new mail */
if ((Sort & SORT_MASK) == SORT_THREADS)
{
if (check == M_REOPENED)
{
HEADER *h;
h = Context->tree;
Context->collapsed = 0;
while (h)
{
mutt_uncollapse_thread (Context, h);
h = h->next;
}
mutt_set_virtual (Context);
}
else if (oldcount)
{
for (j = 0; j < Context->msgcount - oldcount; j++)
{
int k;
for (k = 0; k < Context->msgcount; k++)
{
HEADER *h = Context->hdrs[k];
if (h == save_new[j] && (!Context->pattern || h->limited))
mutt_uncollapse_thread (Context, h);
}
}
FREE (&save_new);
mutt_set_virtual (Context);
}
}
menu->current = -1;
if (oldcount)
{
/* restore the current message to the message it was pointing to */
for (j = 0; j < Context->vcount; j++)
if (Context->hdrs[Context->v2r[j]]->index == menu->oldcurrent)
{
menu->current = j;
break;
}
}
if (menu->current < 0)
menu->current = ci_first_message ();
update_index (menu, Context, check, oldcount, index_hint);
/* notify the user of new mail */
if (check == M_REOPENED)
mutt_error _("Mailbox was externally modified. Flags may be wrong.");
......@@ -452,10 +463,10 @@ int mutt_index_menu (void)
}
/* avoid the message being overwritten by buffy */
do_buffy_notify = 0;
menu->redraw = REDRAW_FULL;
menu->max = Context->vcount;
set_option (OPTSEARCHINVALID);
}
}
......@@ -464,8 +475,6 @@ int mutt_index_menu (void)
imap_disallow_reopen (Context);
#endif
check_lock = 0;
if (!attach_msg)
{
/* check for new mail in the incoming folders */
......@@ -760,16 +769,26 @@ int mutt_index_menu (void)
}
if (query_quadoption (OPT_QUIT, _("Quit Mutt?")) == M_YES)
{
{
int check;
#ifdef USE_IMAP
/* logout gracefully from the IMAP server */
if (Context && Context->magic == M_IMAP)
imap_set_logout (Context);
#endif
if (!Context || mx_close_mailbox (Context) == 0)
oldcount = Context->msgcount;
if (!Context || (check = mx_close_mailbox (Context, &index_hint)) == 0)
done = 1;
else
{
if (check == M_NEW_MAIL || check == M_REOPENED)
update_index (menu, Context, check, oldcount, index_hint);
menu->redraw = REDRAW_FULL; /* new mail arrived? */
set_option (OPTSEARCHINVALID);
}
}
break;
......@@ -806,15 +825,13 @@ int mutt_index_menu (void)
case OP_MAIN_SYNC_FOLDER:
check_lock = 1; /* call mx_check_mailbox() with locking the
* nex time
*/
CHECK_MSGCOUNT;
CHECK_READONLY;
{
int oldvcount = Context->vcount;
int oldcount = Context->msgcount;
int dcount = 0;
int check;
/* calculate the number of messages _above_ the cursor,
* so we can keep the cursor on the current message
......@@ -824,14 +841,18 @@ int mutt_index_menu (void)
if (Context->hdrs[Context->v2r[j]]->deleted)
dcount++;
}
if (mx_sync_mailbox (Context) == 0)
if ((check = mx_sync_mailbox (Context, &index_hint)) == 0)
{
if (Context->vcount != oldvcount)
menu->current -= dcount;
set_option (OPTSEARCHINVALID);
}
/* do a sanity check even if mx_sync_mailbox failed.
else if (check == M_NEW_MAIL || check == M_REOPENED)
update_index (menu, Context, check, oldcount, index_hint);
/*
* do a sanity check even if mx_sync_mailbox failed.
*/
if (menu->current < 0 || menu->current >= Context->vcount)
......@@ -927,10 +948,17 @@ int mutt_index_menu (void)
if (Context)
{
int check;
mutt_str_replace (&LastFolder, Context->path);
oldcount = Context->msgcount;
if (mx_close_mailbox (Context) != 0)
if ((check = mx_close_mailbox (Context, &index_hint)) != 0)
{
if (check == M_NEW_MAIL || check == M_REOPENED)
update_index (menu, Context, check, oldcount, index_hint);
set_option (OPTSEARCHINVALID);
menu->redraw = REDRAW_INDEX | REDRAW_STATUS;
break;
}
......
......@@ -77,7 +77,7 @@ static int edit_one_message (CONTEXT *ctx, HEADER *cur)
rc = mutt_append_message (&tmpctx, ctx, cur, 0, CH_NOLEN | CH_NOSTATUS); oerrno = errno;
mx_close_mailbox (&tmpctx);
mx_close_mailbox (&tmpctx, NULL);
if (rc == -1)
{
......@@ -149,7 +149,7 @@ static int edit_one_message (CONTEXT *ctx, HEADER *cur)
if (msg == NULL)
{
mutt_error (_("Can't append to folder: %s"), strerror (errno));
mx_close_mailbox (&tmpctx);
mx_close_mailbox (&tmpctx, NULL);
goto bail;
}
......@@ -162,7 +162,7 @@ static int edit_one_message (CONTEXT *ctx, HEADER *cur)
rc = mx_commit_message (msg, &tmpctx);
mx_close_message (&msg);
mx_close_mailbox (&tmpctx);
mx_close_mailbox (&tmpctx, NULL);
bail:
if (fp) fclose (fp);
......
......@@ -737,7 +737,7 @@ int imap_select_mailbox (CONTEXT* ctx, const char* path)
return -1;
}
if (imap_sync_mailbox (ctx, 0) < 0)
if (imap_sync_mailbox (ctx, 0, NULL) < 0)
return -1;
idata = CTX_DATA;
......@@ -957,7 +957,7 @@ int imap_make_msg_set (char* buf, size_t buflen, CONTEXT* ctx, int flag,
* expunge: 0 or 1 - do expunge?
*/
int imap_sync_mailbox (CONTEXT* ctx, int expunge)
int imap_sync_mailbox (CONTEXT* ctx, int expunge, int *index_hint)
{
char buf[HUGE_STRING];
char flags[LONG_STRING];
......@@ -977,7 +977,7 @@ int imap_sync_mailbox (CONTEXT* ctx, int expunge)
* expects the context to be changed.
*/
if ((rc = imap_check_mailbox (ctx, NULL)) != 0)
if ((rc = imap_check_mailbox (ctx, index_hint)) != 0)
return rc;
/* if we are expunging anyway, we can do deleted messages very quickly... */
......
......@@ -44,7 +44,7 @@ int imap_open_mailbox (CONTEXT *ctx);
int imap_open_mailbox_append (CONTEXT *ctx);
int imap_select_mailbox (CONTEXT *ctx, const char* path);
void imap_set_logout (CONTEXT *ctx);
int imap_sync_mailbox (CONTEXT *ctx, int expunge);
int imap_sync_mailbox (CONTEXT *ctx, int expunge, int *index_hint);
void imap_fastclose_mailbox (CONTEXT *ctx);
int imap_buffy_check (char *path);
int imap_mailbox_check (char *path, int new);
......
......@@ -51,8 +51,8 @@ MESSAGE *mx_open_new_message (CONTEXT *, HEADER *, int);
void mx_fastclose_mailbox (CONTEXT *);
int mx_close_mailbox (CONTEXT *);
int mx_sync_mailbox (CONTEXT *);
int mx_close_mailbox (CONTEXT *, int *);
int mx_sync_mailbox (CONTEXT *, int *);
int mx_commit_message (MESSAGE *, CONTEXT *);
int mx_close_message (MESSAGE **);
int mx_get_magic (const char *);
......
......@@ -662,11 +662,12 @@ int mbox_check_mailbox (CONTEXT *ctx, int *index_hint)
* 0 success
* -1 failure
*/
int mbox_sync_mailbox (CONTEXT *ctx)
int mbox_sync_mailbox (CONTEXT *ctx, int *index_hint)
{
char tempfile[_POSIX_PATH_MAX];
char buf[32];
int i, j, save_sort = SORT_ORDER;
int rc = -1;
int need_sort = 0; /* flag to resort mailbox if new mail arrives */
int first; /* first message to be written */
long offset; /* location in mailbox to write changed messages */
......@@ -703,10 +704,11 @@ int mbox_sync_mailbox (CONTEXT *ctx)
}
/* Check to make sure that the file hasn't changed on disk */
if ((i = mbox_check_mailbox (ctx, NULL)) == M_NEW_MAIL || i == M_REOPENED)
if ((i = mbox_check_mailbox (ctx, index_hint)) == M_NEW_MAIL || i == M_REOPENED)
{
/* new mail arrived, or mailbox reopened */
need_sort = i;
rc = i;
goto bail;
}
else if (i < 0)
......@@ -957,9 +959,7 @@ int mbox_sync_mailbox (CONTEXT *ctx)
bail: /* Come here in case of disaster */
/* Disadvantage of using "goto" */
if (fp)
fclose (fp);
safe_fclose (&fp);
/* this is ok to call even if we haven't locked anything */
mbox_unlock_mailbox (ctx);
......@@ -982,7 +982,7 @@ bail: /* Come here in case of disaster */
mutt_sort_headers (ctx, (need_sort == M_REOPENED));
}
return (-1);
return rc;
}
/* close a mailbox opened in write-mode */
......
......@@ -715,13 +715,13 @@ static int maildir_sync_message (CONTEXT *ctx, int msgno)
return (0);
}
int mh_sync_mailbox (CONTEXT * ctx)
int mh_sync_mailbox (CONTEXT * ctx, int *index_hint)
{
char path[_POSIX_PATH_MAX], tmp[_POSIX_PATH_MAX];
int i, j;
if (mh_check_mailbox(ctx, NULL) != 0)
return -1;
if ((i = mh_check_mailbox(ctx, index_hint)) != 0)
return i;
for (i = 0; i < ctx->msgcount; i++)
{
......
......@@ -703,7 +703,7 @@ void mx_fastclose_mailbox (CONTEXT *ctx)
}
/* save changes to disk */
static int sync_mailbox (CONTEXT *ctx)
static int sync_mailbox (CONTEXT *ctx, int *index_hint)
{
#ifdef BUFFY_SIZE
BUFFY *tmp = NULL;
......@@ -717,7 +717,7 @@ static int sync_mailbox (CONTEXT *ctx)
{
case M_MBOX:
case M_MMDF:
rc = mbox_sync_mailbox (ctx);
rc = mbox_sync_mailbox (ctx, index_hint);
#ifdef BUFFY_SIZE
tmp = mutt_find_mailbox (ctx->path);
#endif
......@@ -725,13 +725,13 @@ static int sync_mailbox (CONTEXT *ctx)
case M_MH:
case M_MAILDIR:
rc = mh_sync_mailbox (ctx);
rc = mh_sync_mailbox (ctx, index_hint);
break;
#ifdef USE_IMAP
case M_IMAP:
/* extra argument means EXPUNGE */
rc = imap_sync_mailbox (ctx, 1);
rc = imap_sync_mailbox (ctx, 1, index_hint);
break;
#endif /* USE_IMAP */
}
......@@ -749,9 +749,10 @@ static int sync_mailbox (CONTEXT *ctx)
}
/* save changes and close mailbox */
int mx_close_mailbox (CONTEXT *ctx)
int mx_close_mailbox (CONTEXT *ctx, int *index_hint)
{
int i, move_messages = 0, purge = 1, read_msgs = 0;
int check;
int isSpool = 0;
CONTEXT f;
char mbox[_POSIX_PATH_MAX];
......@@ -871,13 +872,13 @@ int mx_close_mailbox (CONTEXT *ctx)
}
else
{
mx_close_mailbox (&f);
mx_close_mailbox (&f, NULL);
return -1;
}
}
}
mx_close_mailbox (&f);
mx_close_mailbox (&f, NULL);
}
}
......@@ -892,8 +893,8 @@ int mx_close_mailbox (CONTEXT *ctx)
/* allow IMAP to preserve the deleted flag across sessions */
if (ctx->magic == M_IMAP)
{
if (imap_sync_mailbox (ctx, purge) != 0)
return -1;
if ((check = imap_sync_mailbox (ctx, purge, index_hint)) != 0)
return check;
}
else
#endif
......@@ -907,8 +908,8 @@ int mx_close_mailbox (CONTEXT *ctx)
if (ctx->changed || ctx->deleted)
{
if (sync_mailbox (ctx) != 0)
return -1;
if ((check = sync_mailbox (ctx, index_hint)) != 0)
return check;
}
}
......@@ -1013,7 +1014,7 @@ void mx_update_tables(CONTEXT *ctx, int committing)
* 0 success
* -1 error
*/
int mx_sync_mailbox (CONTEXT *ctx)
int mx_sync_mailbox (CONTEXT *ctx, int *index_hint)
{
int rc, i;
int purge = 1;
......@@ -1069,10 +1070,10 @@ int mx_sync_mailbox (CONTEXT *ctx)
#ifdef USE_IMAP
if (ctx->magic == M_IMAP)
rc = imap_sync_mailbox (ctx, purge);
rc = imap_sync_mailbox (ctx, purge, index_hint);
else
#endif
rc = sync_mailbox (ctx);
rc = sync_mailbox (ctx, index_hint);
if (rc == 0)
{
#ifdef USE_IMAP
......
......@@ -46,7 +46,7 @@ WHERE short DefaultMagic INITVAL (M_MBOX);
#define KENDRA_SEP "\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\001\n"
#define MAXLOCKATTEMPT 5
int mbox_sync_mailbox (CONTEXT *);
int mbox_sync_mailbox (CONTEXT *, int *);
int mbox_open_mailbox (CONTEXT *);
int mbox_check_mailbox (CONTEXT *, int *);
int mbox_close_mailbox (CONTEXT *);
......@@ -56,7 +56,7 @@ int mmdf_parse_mailbox (CONTEXT *);
void mbox_unlock_mailbox (CONTEXT *);
int mh_read_dir (CONTEXT *, const char *);
int mh_sync_mailbox (CONTEXT *);
int mh_sync_mailbox (CONTEXT *, int *);
int mh_check_mailbox (CONTEXT *, int *);
int mh_parse_sequences (CONTEXT *, const char *);
......
......@@ -317,7 +317,7 @@ void mutt_fetchPopMail (void)
err = 1;
mx_close_message (&msg);
}
mx_close_mailbox (&ctx);
mx_close_mailbox (&ctx, NULL);
if (err)
{
......
......@@ -254,7 +254,7 @@ int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur, char *fcc, size
if (! PostContext->msgcount)
{
PostCount = 0;
mx_close_mailbox (PostContext);
mx_close_mailbox (PostContext, NULL);
#ifdef USE_IMAP
if (need_reopen)
ctx = mx_open_mailbox (curpath, 0, PostContext);
......@@ -272,7 +272,7 @@ int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur, char *fcc, size
}
else if ((h = select_msg ()) == NULL)
{
mx_close_mailbox (PostContext);
mx_close_mailbox (PostContext, NULL);
#ifdef USE_IMAP
if (need_reopen)
ctx = mx_open_mailbox (curpath, 0, PostContext);
......@@ -303,7 +303,7 @@ int mutt_get_postponed (CONTEXT *ctx, HEADER *hdr, HEADER **cur, char *fcc, size
/* avoid the "purge deleted messages" prompt */
opt_delete = quadoption (OPT_DELETE);
set_quadoption (OPT_DELETE, M_YES);
mx_close_mailbox (PostContext);
mx_close_mailbox (PostContext, NULL);
set_quadoption (OPT_DELETE, opt_delete);
#ifdef USE_IMAP
......
......@@ -869,7 +869,7 @@ static ADDRESS *set_reverse_name (ENVELOPE *env)
{
tmp = rfc822_cpy_adr_real (tmp);
if (!option (OPTREVREAL))
safe_free (&tmp->personal);
FREE (&tmp->personal);
if (!tmp->personal)
tmp->personal = safe_strdup (Realname);
}
......
......@@ -1959,7 +1959,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
if ((tempfp = safe_fopen (tempfile, "w+")) == NULL)
{
mutt_perror (tempfile);
mx_close_mailbox (&f);
mx_close_mailbox (&f, NULL);
return (-1);
}
}
......@@ -1967,7 +1967,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
if ((msg = mx_open_new_message (&f, hdr, M_ADD_FROM)) == NULL)
{
mx_close_mailbox (&f);
mx_close_mailbox (&f, NULL);
return (-1);
}
......@@ -2056,7 +2056,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
unlink (tempfile);
mx_commit_message (msg, &f); /* XXX - really? */
mx_close_message (&msg);
mx_close_mailbox (&f);
mx_close_mailbox (&f, NULL);
return -1;
}
......@@ -2085,7 +2085,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
if (mx_commit_message (msg, &f) != 0)
r = -1;
mx_close_message (&msg);
mx_close_mailbox (&f);
mx_close_mailbox (&f, NULL);
return r;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment