// Room functions
//
-// Copyright (c) 1996-2022 by the citadel.org team
+// Copyright (c) 1996-2023 by the citadel.org team
//
// This program is open source software. Use, duplication, or
// disclosure are subject to the GNU General Public License v3.
#include "webcit.h"
-// Return a "zero-terminated" array of message numbers in the current room.
+// Return an array of message numbers in the current room.
// Caller owns the memory and must free it. Returns NULL if any problems.
-long *get_msglist(struct ctdlsession *c, char *which_msgs) {
+Array *get_msglist(struct ctdlsession *c, char *which_msgs) {
char buf[1024];
- long *msglist = NULL;
- int num_msgs = 0;
- int num_alloc = 0;
+ Array *msglist = NULL;
+
+ msglist = array_new(sizeof(long));
+ if (msglist == NULL) {
+ return(NULL);
+ }
ctdl_printf(c, "MSGS %s", which_msgs);
ctdl_readline(c, buf, sizeof(buf));
if (buf[0] == '1') {
- do {
- if (num_msgs >= num_alloc) {
- if (num_alloc == 0) {
- num_alloc = 1024;
- msglist = malloc(num_alloc * sizeof(long));
- }
- else {
- num_alloc *= 2;
- msglist = realloc(msglist, num_alloc * sizeof(long));
- }
- }
- ctdl_readline(c, buf, sizeof(buf));
- msglist[num_msgs++] = atol(buf);
- } while (strcmp(buf, "000")); // this makes the last element a "0" terminator
+ while (ctdl_readline(c, buf, sizeof(buf)), strcmp(buf, "000")) {
+ long m = atol(buf);
+ array_append(msglist, &m);
+ }
}
+
return msglist;
}
for (i = 0; i < num_tags; ++i) {
extract_token(tag, taglist, i, ',', sizeof tag);
- striplt(tag);
+ string_trim(tag);
char *lq = (strchr(tag, '"'));
char *rq = (strrchr(tag, '"'));
if (lq < rq) { // has two double quotes
strcpy(rq, "");
strcpy(tag, ++lq);
}
- striplt(tag);
+ string_trim(tag);
if (!strcmp(tag, "*")) { // wildcard match
return (1);
}
ctdl_readline(c, buf, sizeof(buf));
if (buf[0] == '1') {
while (ctdl_readline(c, buf, sizeof(buf)), (strcmp(buf, "000"))) {
- utf8ify_rfc822_string(buf);
JsonValue *jmsg = NewJsonObject(HKEY("message"));
JsonObjectAppend(jmsg, NewJsonNumber(HKEY("msgnum"), extract_long(buf, 0)));
JsonObjectAppend(jmsg, NewJsonNumber(HKEY("time"), extract_long(buf, 1)));
extract_token(field, buf, 2, '|', sizeof field);
+ utf8ify_rfc822_string(field);
JsonObjectAppend(jmsg, NewJsonPlainString(HKEY("author"), field, -1));
extract_token(field, buf, 4, '|', sizeof field);
+ utf8ify_rfc822_string(field);
JsonObjectAppend(jmsg, NewJsonPlainString(HKEY("addr"), field, -1));
extract_token(field, buf, 5, '|', sizeof field);
+ utf8ify_rfc822_string(field);
JsonObjectAppend(jmsg, NewJsonPlainString(HKEY("subject"), field, -1));
JsonObjectAppend(jmsg, NewJsonNumber(HKEY("msgidhash"), extract_long(buf, 6)));
extract_token(field, buf, 7, '|', sizeof field);
+ utf8ify_rfc822_string(field);
JsonObjectAppend(jmsg, NewJsonPlainString(HKEY("references"), field, -1));
JsonArrayAppend(j, jmsg); // add the message to the array
}
// Client is requesting a message list
void json_msglist(struct http_transaction *h, struct ctdlsession *c, char *which) {
int i = 0;
- long *msglist = get_msglist(c, which);
+ Array *msglist = get_msglist(c, which);
JsonValue *j = NewJsonArray(HKEY("msgs"));
if (msglist != NULL) {
- for (i = 0; msglist[i] > 0; ++i) {
- JsonArrayAppend(j, NewJsonNumber(HKEY("m"), msglist[i]));
+ for (i = 0; i < array_len(msglist); ++i) {
+ long m;
+ memcpy(&m, array_get_element_at(msglist, i), sizeof(long));
+ JsonArrayAppend(j, NewJsonNumber(HKEY("m"), m));
}
- free(msglist);
+ array_free(msglist);
}
StrBuf *sj = NewStrBuf();
return;
}
+ if (!strncasecmp(buf, "calendar:", 9)) { // Client is requesting a calendar range
+ unescape_input(&buf[9]);
+ calendar_msglist(h, c, &buf[9]);
+ return;
+ }
+
if (!strcasecmp(buf, "info.txt")) { // Client is requesting the room info banner
read_room_info_banner(h, c);
return;
// A sixth component in the URL can be one of two things:
// (1) a MIME part specifier, in which case the client wants to download that component within the message
// (2) a content-type, in which ase the client wants us to try to render it a certain way
- if (num_tokens(h->url, '/') == 6) {
+ if (num_tokens(h->url, '/') >= 6) {
extract_token(buf, h->url, 5, '/', sizeof buf);
if (!IsEmptyStr(buf)) {
if (!strcasecmp(buf, "json")) {
return;
}
- // DOOOOOO ITTTTT!!!
-
+ // Now perform the requested operation.
if (!strcasecmp(h->method, "DELETE")) {
dav_delete_message(h, c, msgnum);
}
else if (!strcasecmp(h->method, "PUT")) {
dav_put_message(h, c, unescaped_euid, msgnum);
}
+ else if (!strcasecmp(h->method, "MOVE")) {
+ dav_move_or_copy_message(h, c, msgnum, DAV_MOVE);
+ }
+ else if (!strcasecmp(h->method, "COPY")) {
+ dav_move_or_copy_message(h, c, msgnum, DAV_COPY);
+ }
else {
- do_404(h); // Got this far but the method made no sense? Bummer.
+ do_404(h); // We should never get here, but if we do, a 404 error is clean.
}
-
}
return;
}
- do_404(h); // future implementations like CardDAV will require code paths here
+ do_404(h); // future implementations like CardDAV will require code paths here
}
else {
add_response_header(h, strdup("DAV"), strdup("1")); // ordinary WebDAV for all other room types
}
- add_response_header(h, strdup("Allow"), strdup("OPTIONS, PROPFIND, GET, PUT, REPORT, DELETE"));
+ add_response_header(h, strdup("Allow"), strdup("OPTIONS, PROPFIND, GET, PUT, REPORT, DELETE, MOVE, COPY"));
}
syslog(LOG_DEBUG, "Client PROPFIND requested depth: %d", dav_depth);
StrBuf *Buf = NewStrBuf();
- StrBufAppendPrintf(Buf, "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
- "<D:multistatus " "xmlns:D=\"DAV:\" " "xmlns:C=\"urn:ietf:params:xml:ns:caldav\"" ">");
+ StrBufAppendPrintf(Buf,
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<D:multistatus "
+ "xmlns:D=\"DAV:\" "
+ "xmlns:C=\"urn:ietf:params:xml:ns:caldav\""
+ ">"
+ );
// Transmit the collection resource
StrBufAppendPrintf(Buf, "<D:response>");
// If a depth greater than zero was specified, transmit the collection listing
// BEGIN COLLECTION
if (dav_depth > 0) {
- long *msglist = get_msglist(c, "ALL");
+ Array *msglist = get_msglist(c, "ALL");
if (msglist) {
int i;
- for (i = 0; (msglist[i] > 0); ++i) {
- if ((i % 10) == 0)
+ for (i = 0; i < array_len(msglist); ++i) {
+ if ((i % 10) == 0) {
syslog(LOG_DEBUG, "PROPFIND enumerated %d messages", i);
- e = NULL; // EUID gets stored here
+ }
+ e = NULL; // EUID gets stored here
timestamp = 0;
+ long m;
+ memcpy(&m, array_get_element_at(msglist, i), sizeof(long));
+
char cbuf[1024];
- ctdl_printf(c, "MSG0 %ld|3", msglist[i]);
+ ctdl_printf(c, "MSG0 %ld|3", m);
ctdl_readline(c, cbuf, sizeof(cbuf));
if (cbuf[0] == '1')
while (ctdl_readline(c, cbuf, sizeof(cbuf)), strcmp(cbuf, "000")) {
}
if (e == NULL) {
e = malloc(20);
- sprintf(e, "%ld", msglist[i]);
+ sprintf(e, "%ld", m);
}
StrBufAppendPrintf(Buf, "<D:response>");
free(datestring);
}
if (enumerate_by_euid) { // FIXME ajc 2017oct30 should this be inside the timestamp conditional?
- StrBufAppendPrintf(Buf, "<D:getetag>\"%ld\"</D:getetag>", msglist[i]);
+ StrBufAppendPrintf(Buf, "<D:getetag>\"%ld\"</D:getetag>", m);
}
}
StrBufAppendPrintf(Buf, "</D:prop></D:propstat></D:response>\n");
free(e);
}
- free(msglist);
+ array_free(msglist);
};
}
// END COLLECTION
JsonObjectAppend(j, NewJsonNumber(HKEY("current_view"), c->room_current_view));
JsonObjectAppend(j, NewJsonNumber(HKEY("default_view"), c->room_default_view));
JsonObjectAppend(j, NewJsonNumber(HKEY("is_room_aide"), c->is_room_aide));
+ JsonObjectAppend(j, NewJsonNumber(HKEY("is_trash_folder"), c->is_trash_folder));
JsonObjectAppend(j, NewJsonNumber(HKEY("can_delete_messages"), c->can_delete_messages));
JsonObjectAppend(j, NewJsonNumber(HKEY("new_messages"), c->new_messages));
JsonObjectAppend(j, NewJsonNumber(HKEY("total_messages"), c->total_messages));
JsonObjectAppend(j, NewJsonNumber(HKEY("last_seen"), c->last_seen));
JsonObjectAppend(j, NewJsonNumber(HKEY("room_mtime"), c->room_mtime));
- JsonObjectAppend(j, NewJsonNumber(HKEY("new_mail"), c->new_mail));
StrBuf *sj = NewStrBuf();
SerializeJson(sj, j, 1); // '1' == free the source array
c->last_seen = extract_long(&buf[4], 6); // The highest message number the user has read in this room
// 7 (int)rmailflag Boolean flag: 1 if this is a Mail> room, 0 otherwise.
c->is_room_aide = extract_int(&buf[4], 8);
- c->new_mail = extract_int(&buf[4], 9); // the number of new messages in the user's INBOX
+ // 9 This position is no longer used
// 10 (int)CC->room.QRfloor The floor number this room resides on
c->room_current_view = extract_int(&buf[4], 11);
c->room_default_view = extract_int(&buf[4], 12);
- // 13 (int)is_trash Boolean flag: 1 if this is the user's Trash folder, 0 otherwise.
+ c->is_trash_folder = extract_int(&buf[4], 13); // Boolean flag: 1 if this is the user's Trash folder, 0 otherwise.
room_flags2 = extract_long(&buf[4], 14); // More flags associated with this room.
c->room_mtime = extract_long(&buf[4], 15); // Timestamp of the last write activity in this room
}
return;
}
- if (num_tokens(h->url, '/') == 6) {
+ if (num_tokens(h->url, '/') >= 6) {
object_in_room(h, c); // /ctdl/r/roomname/object/ or possibly /ctdl/r/roomname/object/component
return;
}