jcs
/subtext
/amendments
/71
mail: Add missing read field, implement message listing and reading
jcs made amendment 71 over 2 years ago
--- mail.c Sat Jan 29 18:36:52 2022
+++ mail.c Thu Feb 17 12:16:02 2022
@@ -34,6 +34,8 @@ struct bile_object_field mail_object_fields[] = {
member_size(struct private_message, id), -1 },
{ offsetof(struct private_message, time),
member_size(struct private_message, time), -1 },
+ { offsetof(struct private_message, read),
+ member_size(struct private_message, read), -1 },
{ offsetof(struct private_message, sender_user_id),
member_size(struct private_message, sender_user_id), -1 },
{ offsetof(struct private_message, subject_size),
@@ -51,12 +53,19 @@ struct bile_object_field mail_object_fields[] = {
short mail_save(struct session *s, struct private_message *msg);
size_t mail_find_for_user_id(unsigned long user_id,
unsigned long **mail_ids);
+void mail_help(struct session *s);
+short mail_read(struct session *s, unsigned long idx);
+void mail_list(struct session *s, bool sent, unsigned long **mail_ids,
+ size_t *nmsgs);
void
mail_menu(struct session *s)
{
+ char arg[32];
+ size_t count, nmsgs, id;
bool done = false;
- unsigned char c;
+ char *field;
+ unsigned long *mail_ids = NULL;
if (!s->user) {
session_output_string(s, "Mail is not available to guests.\r\n"
@@ -65,53 +74,61 @@ mail_menu(struct session *s)
return;
}
- session_output_template(s, "{{B}}Private Mail Menu{{/}}\r\n"
- "{{B}}---------{{/}}\r\n");
-
-mail_menu:
- session_output_template(s, "({{B}}L{{/}})ist mail messages\r\n"
- "({{B}}C{{/}})ompose new message\r\n"
- "({{B}}Q{{/}})uit to main menu\r\n");
+ session_output_template(s, "{{B}}Private Mail{{/}} "
+ "('{{B}}?{{/}}' for help)\r\n"
+ "{{B}}--------------------{{/}}\r\n");
session_flush(s);
+ mail_list(s, false, &mail_ids, &nmsgs);
+
while (!done) {
session_output_template(s, "{{B}}Mail>{{/}} ");
session_flush(s);
-get_another_char:
- c = session_input_char(s);
- if (s->ending)
+ field = session_field_input(s, 30, 30, NULL, 0);
+ if (field == NULL)
return;
- if (c == '\r' || c == 0)
- goto get_another_char;
-
- session_printf(s, "%c\r\n", c);
+ session_output(s, "\r\n", 2);
session_flush(s);
- switch (c) {
- case 'c':
- case 'C':
- case 's':
- case 'S':
+ if (sscanf(field, "c %s%n", &arg, &count) == 1 && count > 1)
+ mail_compose(s, arg, NULL, NULL);
+ else if (sscanf(field, "%ld%n", &id, &count) == 1 && count > 1) {
+ if (id < 1 || id > nmsgs) {
+ session_output_string(s, "Invalid message id\r\n");
+ session_flush(s);
+ continue;
+ }
+ mail_read(s, mail_ids[id - 1]);
+ } else if (strcmp(field, "c") == 0)
mail_compose(s, NULL, NULL, NULL);
- break;
- case 'l':
- case 'L':
- mail_list(s, false);
- break;
- case 'Q':
- case 'q':
- case 'X':
- case 'x':
+ else if (strcmp(field, "l") == 0)
+ mail_list(s, false, &mail_ids, &nmsgs);
+ else if (strcmp(field, "q") == 0)
done = true;
- break;
- case '?':
- goto mail_menu;
+ else if (strcmp(field, "?") == 0)
+ mail_help(s);
+ else if (field[0] != '\0') {
+ session_output_template(s,
+ "Invalid option ({{B}}?{{/}} for help)\r\n");
+ session_flush(s);
}
+
+ free(field);
}
}
void
+mail_help(struct session *s)
+{
+ session_output_template(s,
+ "({{B}}L{{/}})ist and read mail messages\r\n"
+ "({{B}}C{{/}})ompose new message\r\n"
+ "({{B}}Q{{/}})uit to main menu\r\n");
+ session_flush(s);
+}
+
+void
mail_compose(struct session *s, char *initial_to, char *initial_subject,
char *initial_body)
{
@@ -264,38 +281,44 @@ mail_compose_done:
}
void
-mail_list(struct session *s, bool sent)
+mail_list(struct session *s, bool sent, unsigned long **mail_ids,
+ size_t *nmsgs)
{
- unsigned long *mail_ids = NULL;
- size_t nmsgs, n, size;
+ char time[24];
+ size_t n, size;
struct private_message msg;
struct user *user;
- char *data;
+ char *data, *input;
+ short id;
- nmsgs = mail_find_for_user_id(s->user->id, &mail_ids);
- if (nmsgs == 0) {
+ *nmsgs = mail_find_for_user_id(s->user->id, mail_ids);
+ if (*nmsgs == 0) {
session_output_string(s, "No messages.\r\n");
session_flush(s);
- if (mail_ids)
- free(mail_ids);
+ if (*mail_ids)
+ free(*mail_ids);
return;
}
- for (n = 0; n < nmsgs; n++) {
- size = bile_read_alloc(db->bile, DB_MESSAGE_RTYPE, mail_ids[n],
+ for (n = 0; n < *nmsgs; n++) {
+ size = bile_read_alloc(db->bile, DB_MESSAGE_RTYPE, (*mail_ids)[n],
&data);
bile_unmarshall_object(db->bile, mail_object_fields,
nitems(mail_object_fields), data, size, &msg);
user = user_find(msg.sender_user_id);
- session_printf(s, "%s%c [% 3ld] %s % 16s %s%s\r\n",
- msg.read ? ansi(s, ANSI_BOLD, ANSI_END) : "",
- msg.read ? 'N' : ' ',
+ strftime(time, sizeof(time), "%Y-%m-%d %H:%M",
+ localtime(&msg.time));
+
+ session_printf(s, "%s%c [%- 3ld] %s %- 10s %- 40s%s\r\n",
+ msg.read ? "" : ansi(s, ANSI_BOLD, ANSI_END),
+ msg.read ? ' ' : 'N',
n + 1,
- "(time)",
+ time,
user ? user->username : "(unknown)",
msg.subject,
- msg.read ? ansi(s, ANSI_RESET, ANSI_END) : "");
+ msg.read ? "" : ansi(s, ANSI_RESET, ANSI_END));
+ session_flush(s);
if (msg.subject)
free(msg.subject);
@@ -303,8 +326,47 @@ mail_list(struct session *s, bool sent)
free(msg.body);
free(data);
}
+}
+
+short
+mail_read(struct session *s, unsigned long id)
+{
+ char time[32];
+ size_t size;
+ struct private_message msg;
+ struct user *sender, *recipient;
+ char *data;
- free(mail_ids);
+ size = bile_read_alloc(db->bile, DB_MESSAGE_RTYPE, id, &data);
+ bile_unmarshall_object(db->bile, mail_object_fields,
+ nitems(mail_object_fields), data, size, &msg);
+ sender = user_find(msg.sender_user_id);
+ recipient = user_find(msg.recipient_user_id);
+
+ strftime(time, sizeof(time), "%Y-%m-%d %H:%M:%S",
+ localtime(&msg.time));
+
+ session_printf(s, "{{B}}From:{{/}} %s\r\n",
+ sender ? sender->username : "(unknown)");
+ session_printf(s, "{{B}}To:{{/}} %s\r\n",
+ recipient ? recipient->username : "(unknown)");
+ session_printf(s, "{{B}}Date:{{/}} %s\r\n", time);
+ session_printf(s, "{{B}}Subject:{{/}} %s\r\n", msg.subject);
+ session_flush(s);
+ session_output_string(s, "\r\n");
+ session_output(s, msg.body, msg.body_size);
+ session_output_string(s, "\r\n");
+
+ if (!msg.read) {
+ msg.read = Time;
+ mail_save(s, &msg);
+ }
+
+ if (msg.subject)
+ free(msg.subject);
+ if (msg.body)
+ free(msg.body);
+ free(data);
}
short
@@ -333,9 +395,10 @@ mail_find_for_user_id(unsigned long user_id, unsigned
unsigned long msg_user_id;
struct user_map *muser;
struct bile_object *o;
- size_t nmsg, msgs_for_user, mail_ids_size;
+ size_t nmsg, msgs_for_user, mail_ids_size, id;
+ short i, j;
- mail_ids_size = 256;
+ mail_ids_size = sizeof(unsigned long) * 16;
*mail_ids = xmalloc(mail_ids_size);
msgs_for_user = 0;
@@ -345,12 +408,24 @@ mail_find_for_user_id(unsigned long user_id, unsigned
sizeof(msg_user_id));
if (msg_user_id == user_id) {
EXPAND_TO_FIT(*mail_ids, mail_ids_size,
- msgs_for_user * sizeof(long), sizeof(long), 256);
+ msgs_for_user * sizeof(long), sizeof(long),
+ sizeof(unsigned long) * 16);
(*mail_ids)[msgs_for_user++] = o->id;
}
free(o);
nmsg++;
}
+ /* sort by message id for consistent ordering */
+ for (i = 0; i < msgs_for_user; i++) {
+ for (j = 0; j < msgs_for_user - i - 1; j++) {
+ if ((*mail_ids)[j] > (*mail_ids)[j + 1]) {
+ id = (*mail_ids)[j];
+ (*mail_ids)[j] = (*mail_ids)[j + 1];
+ (*mail_ids)[j + 1] = id;
+ }
+ }
+ }
+
return msgs_for_user;
}
--- mail.h Sat Jan 29 17:29:30 2022
+++ mail.h Sat Jan 29 23:36:48 2022
@@ -39,6 +39,5 @@ extern struct bile_object_field mail_object_fields[];
void mail_menu(struct session *s);
void mail_compose(struct session *s, char *to, char *subject, char *body);
-void mail_list(struct session *s, bool sent);
#endif