2 * represent messages to the citadel clients
4 * Copyright (c) 1987-2017 by the citadel.org team
6 * This program is open source software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
16 #include <libcitadel.h>
18 #include "citserver.h"
19 #include "ctdl_module.h"
20 #include "internet_addressing.h"
25 extern char *msgkeys[];
29 * Back end for the MSGS command: output message number only.
31 void simple_listing(long msgnum, void *userdata)
33 cprintf("%ld\n", msgnum);
38 * Back end for the MSGS command: output header summary.
40 void headers_listing(long msgnum, void *userdata)
42 struct CtdlMessage *msg;
43 int output_mode = *(int *)userdata;
45 msg = CtdlFetchMessage(msgnum, 0, 1);
47 cprintf("%ld|0|||||||\n", msgnum);
51 // change all vertical bars in the subject to hyphens so it doesn't screw up the protocol
52 if (!CM_IsEmpty(msg, eMsgSubject)) {
54 for (p=msg->cm_fields[eMsgSubject]; *p; p++) {
62 // output all fields except the references hash
63 cprintf("%ld|%s|%s|%s|%s|%s",
65 (!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0"),
66 (!CM_IsEmpty(msg, eAuthor) ? msg->cm_fields[eAuthor] : ""),
67 (!CM_IsEmpty(msg, eNodeName) ? msg->cm_fields[eNodeName] : ""),
68 (!CM_IsEmpty(msg, erFc822Addr) ? msg->cm_fields[erFc822Addr] : ""),
69 (!CM_IsEmpty(msg, eMsgSubject) ? msg->cm_fields[eMsgSubject] : "")
72 if (output_mode == MSG_HDRS_THREADS) { // field view with thread hashes
74 // output the references hash
76 (!CM_IsEmpty(msg, emessageId) ? HashLittle(msg->cm_fields[emessageId],strlen(msg->cm_fields[emessageId])) : 0)
79 // output the references hash (yes it's ok that we're trashing the source buffer by doing this)
80 if (!CM_IsEmpty(msg, eWeferences)) {
82 char *rest = msg->cm_fields[eWeferences];
84 while((token = strtok_r(rest, "|", &rest))) {
85 cprintf("%d%s", HashLittle(token,rest-prev-(*rest==0?0:1)), (*rest==0?"":","));
93 else { // field view with no threads, subject extends out forever
100 typedef struct _msg_filter{
106 void headers_brief_filter(long msgnum, void *userdata)
109 struct CtdlMessage *msg;
110 msg_filter *flt = (msg_filter*) userdata;
112 l = GetCount(flt->Filter);
113 msg = CtdlFetchMessage(msgnum, 0, 1);
114 StrBufPrintf(flt->buffer, "%ld", msgnum);
116 for (i = 0; i < l; i++) {
117 StrBufAppendBufPlain(flt->buffer, HKEY("|"), 0);
124 RewindHashPos(flt->Filter, flt->p, 0);
125 while (GetNextHashPos(flt->Filter, flt->p, &len, &k, &v)) {
126 eMsgField f = (eMsgField) v;
128 StrBufAppendBufPlain(flt->buffer, HKEY("|"), 0);
129 if (!CM_IsEmpty(msg, f)) {
130 StrBufAppendBufPlain(flt->buffer, CM_KEY(msg, f), 0);
134 StrBufAppendBufPlain(flt->buffer, HKEY("\n"), 0);
135 cputbuf(flt->buffer);
139 * Back end for the MSGS command: output EUID header.
141 void headers_euid(long msgnum, void *userdata)
143 struct CtdlMessage *msg;
145 msg = CtdlFetchMessage(msgnum, 0, 1);
147 cprintf("%ld||\n", msgnum);
151 cprintf("%ld|%s|%s\n",
153 (!CM_IsEmpty(msg, eExclusiveID) ? msg->cm_fields[eExclusiveID] : ""),
154 (!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0"));
161 * cmd_msgs() - get list of message #'s in this room
162 * implements the MSGS server command using CtdlForEachMessage()
164 void cmd_msgs(char *cmdbuf)
172 int with_template = 0;
173 struct CtdlMessage *template = NULL;
175 char search_string[1024];
176 ForEachMsgCallback CallBack;
178 if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
180 extract_token(which, cmdbuf, 0, '|', sizeof which);
181 cm_ref = extract_int(cmdbuf, 1);
182 extract_token(search_string, cmdbuf, 1, '|', sizeof search_string);
183 with_template = extract_int(cmdbuf, 2);
184 int output_mode = extract_int(cmdbuf, 3);
185 switch (output_mode) {
188 CallBack = simple_listing;
191 case MSG_HDRS_THREADS:
192 CallBack = headers_listing;
195 CallBack = headers_euid;
197 case MSG_HDRS_BRIEFFILTER:
199 CallBack = headers_brief_filter;
204 if (!strncasecmp(which, "OLD", 3))
206 else if (!strncasecmp(which, "NEW", 3))
208 else if (!strncasecmp(which, "FIRST", 5))
210 else if (!strncasecmp(which, "LAST", 4))
212 else if (!strncasecmp(which, "GT", 2))
214 else if (!strncasecmp(which, "LT", 2))
216 else if (!strncasecmp(which, "SEARCH", 6))
221 if ( (mode == MSGS_SEARCH) && (!CtdlGetConfigInt("c_enable_fulltext")) ) {
222 cprintf("%d Full text index is not enabled on this server.\n",
223 ERROR + CMD_NOT_SUPPORTED);
227 if (with_template == 1) {
230 cprintf("%d Send template then receive message list\n",
232 template = (struct CtdlMessage *)
233 malloc(sizeof(struct CtdlMessage));
234 memset(template, 0, sizeof(struct CtdlMessage));
235 template->cm_magic = CTDLMESSAGE_MAGIC;
236 template->cm_anon_type = MES_NORMAL;
238 while(client_getln(buf, sizeof buf) >= 0 && strcmp(buf,"000")) {
242 tValueLen = extract_token(tfield, buf, 0, '|', sizeof tfield);
243 if ((tValueLen == 4) && GetFieldFromMnemonic(&f, tfield))
245 tValueLen = extract_token(tvalue, buf, 1, '|', sizeof tvalue);
246 if (tValueLen >= 0) {
247 CM_SetField(template, f, tvalue, tValueLen);
253 else if (with_template == 2) {
256 cprintf("%d Send list of headers\n",
258 filt.Filter = NewHash(1, lFlathash);
259 filt.buffer = NewStrBufPlain(NULL, 1024);
260 while(client_getln(buf, sizeof buf) >= 0 && strcmp(buf,"000")) {
263 if (GetFieldFromMnemonic(&f, buf))
265 Put(filt.Filter, LKEY(i), (void*)f, reference_free_handler);
269 filt.p = GetNewHashPos(filt.Filter, 0);
273 cprintf("%d \n", LISTING_FOLLOWS);
276 if (with_template < 2) {
277 CtdlForEachMessage(mode,
278 ( (mode == MSGS_SEARCH) ? 0 : cm_ref ),
279 ( (mode == MSGS_SEARCH) ? search_string : NULL ),
284 if (template != NULL) CM_Free(template);
287 CtdlForEachMessage(mode,
288 ( (mode == MSGS_SEARCH) ? 0 : cm_ref ),
289 ( (mode == MSGS_SEARCH) ? search_string : NULL ),
294 DeleteHashPos(&filt.p);
295 DeleteHash(&filt.Filter);
296 FreeStrBuf(&filt.buffer);
303 * display a message (mode 0 - Citadel proprietary)
305 void cmd_msg0(char *cmdbuf)
308 int headers_only = HEADERS_ALL;
310 msgid = extract_long(cmdbuf, 0);
311 headers_only = extract_int(cmdbuf, 1);
313 CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0, NULL, 0, NULL, NULL, NULL);
319 * display a message (mode 2 - RFC822)
321 void cmd_msg2(char *cmdbuf)
324 int headers_only = HEADERS_ALL;
326 msgid = extract_long(cmdbuf, 0);
327 headers_only = extract_int(cmdbuf, 1);
329 CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1, NULL, 0, NULL, NULL, NULL);
335 * display a message (mode 3 - IGnet raw format - internal programs only)
337 void cmd_msg3(char *cmdbuf)
340 struct CtdlMessage *msg = NULL;
343 if (CC->internal_pgm == 0) {
344 cprintf("%d This command is for internal programs only.\n",
345 ERROR + HIGHER_ACCESS_REQUIRED);
349 msgnum = extract_long(cmdbuf, 0);
350 msg = CtdlFetchMessage(msgnum, 1, 1);
352 cprintf("%d Message %ld not found.\n",
353 ERROR + MESSAGE_NOT_FOUND, msgnum);
357 CtdlSerializeMessage(&smr, msg);
361 cprintf("%d Unable to serialize message\n",
362 ERROR + INTERNAL_ERROR);
366 cprintf("%d %ld\n", BINARY_FOLLOWS, (long)smr.len);
367 client_write((char *)smr.ser, (int)smr.len);
374 * Display a message using MIME content types
376 void cmd_msg4(char *cmdbuf)
381 msgid = extract_long(cmdbuf, 0);
382 extract_token(section, cmdbuf, 1, '|', sizeof section);
383 CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0, (section[0] ? section : NULL) , 0, NULL, NULL, NULL);
389 * Client tells us its preferred message format(s)
391 void cmd_msgp(char *cmdbuf)
393 if (!strcasecmp(cmdbuf, "dont_decode")) {
394 CC->msg4_dont_decode = 1;
395 cprintf("%d MSG4 will not pre-decode messages.\n", CIT_OK);
398 safestrncpy(CC->preferred_formats, cmdbuf, sizeof(CC->preferred_formats));
399 cprintf("%d Preferred MIME formats have been set.\n", CIT_OK);
405 * Open a component of a MIME message as a download file
407 void cmd_opna(char *cmdbuf)
410 char desired_section[128];
412 msgid = extract_long(cmdbuf, 0);
413 extract_token(desired_section, cmdbuf, 1, '|', sizeof desired_section);
414 safestrncpy(CC->download_desired_section, desired_section,
415 sizeof CC->download_desired_section);
416 CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1, NULL, 0, NULL, NULL, NULL);
421 * Open a component of a MIME message and transmit it all at once
423 void cmd_dlat(char *cmdbuf)
426 char desired_section[128];
428 msgid = extract_long(cmdbuf, 0);
429 extract_token(desired_section, cmdbuf, 1, '|', sizeof desired_section);
430 safestrncpy(CC->download_desired_section, desired_section,
431 sizeof CC->download_desired_section);
432 CtdlOutputMsg(msgid, MT_SPEW_SECTION, 0, 1, 1, NULL, 0, NULL, NULL, NULL);
436 * message entry - mode 0 (normal)
438 void cmd_ent0(char *entargs)
440 struct CitContext *CCC = CC;
445 char supplied_euid[128];
448 char newusername[256];
449 char newuseremail[256];
450 struct CtdlMessage *msg;
454 recptypes *valid = NULL;
455 recptypes *valid_to = NULL;
456 recptypes *valid_cc = NULL;
457 recptypes *valid_bcc = NULL;
459 int subject_required = 0;
464 int newuseremail_ok = 0;
465 char references[SIZ];
470 post = extract_int(entargs, 0);
471 extract_token(recp, entargs, 1, '|', sizeof recp);
472 anon_flag = extract_int(entargs, 2);
473 format_type = extract_int(entargs, 3);
474 extract_token(subject, entargs, 4, '|', sizeof subject);
475 extract_token(newusername, entargs, 5, '|', sizeof newusername);
476 do_confirm = extract_int(entargs, 6);
477 extract_token(cc, entargs, 7, '|', sizeof cc);
478 extract_token(bcc, entargs, 8, '|', sizeof bcc);
479 switch(CC->room.QRdefaultview) {
483 extract_token(supplied_euid, entargs, 9, '|', sizeof supplied_euid);
486 supplied_euid[0] = 0;
489 extract_token(newuseremail, entargs, 10, '|', sizeof newuseremail);
490 extract_token(references, entargs, 11, '|', sizeof references);
491 for (ptr=references; *ptr != 0; ++ptr) {
492 if (*ptr == '!') *ptr = '|';
495 /* first check to make sure the request is valid. */
497 err = CtdlDoIHavePermissionToPostInThisRoom(
502 (!IsEmptyStr(references)) /* is this a reply? or a top-level post? */
506 cprintf("%d %s\n", err, errmsg);
510 /* Check some other permission type things. */
512 if (IsEmptyStr(newusername)) {
513 strcpy(newusername, CCC->user.fullname);
515 if ( (CCC->user.axlevel < AxAideU)
516 && (strcasecmp(newusername, CCC->user.fullname))
517 && (strcasecmp(newusername, CCC->cs_inet_fn))
519 cprintf("%d You don't have permission to author messages as '%s'.\n",
520 ERROR + HIGHER_ACCESS_REQUIRED,
527 if (IsEmptyStr(newuseremail)) {
531 if (!IsEmptyStr(newuseremail)) {
532 if (!strcasecmp(newuseremail, CCC->cs_inet_email)) {
535 else if (!IsEmptyStr(CCC->cs_inet_other_emails)) {
536 j = num_tokens(CCC->cs_inet_other_emails, '|');
537 for (i=0; i<j; ++i) {
538 extract_token(buf, CCC->cs_inet_other_emails, i, '|', sizeof buf);
539 if (!strcasecmp(newuseremail, buf)) {
546 if (!newuseremail_ok) {
547 cprintf("%d You don't have permission to author messages as '%s'.\n",
548 ERROR + HIGHER_ACCESS_REQUIRED,
554 CCC->cs_flags |= CS_POSTING;
556 /* In mailbox rooms we have to behave a little differently --
557 * make sure the user has specified at least one recipient. Then
558 * validate the recipient(s). We do this for the Mail> room, as
559 * well as any room which has the "Mailbox" view set - unless it
560 * is the DRAFTS room which does not require recipients
563 if ( ( ( (CCC->room.QRflags & QR_MAILBOX) && (!strcasecmp(&CCC->room.QRname[11], MAILROOM)) )
564 || ( (CCC->room.QRflags & QR_MAILBOX) && (CCC->curr_view == VIEW_MAILBOX) )
565 ) && (strcasecmp(&CCC->room.QRname[11], USERDRAFTROOM)) !=0 ) {
566 if (CCC->user.axlevel < AxProbU) {
567 strcpy(recp, "sysop");
572 valid_to = validate_recipients(recp, NULL, 0);
573 if (valid_to->num_error > 0) {
574 cprintf("%d %s\n", ERROR + NO_SUCH_USER, valid_to->errormsg);
575 free_recipients(valid_to);
579 valid_cc = validate_recipients(cc, NULL, 0);
580 if (valid_cc->num_error > 0) {
581 cprintf("%d %s\n", ERROR + NO_SUCH_USER, valid_cc->errormsg);
582 free_recipients(valid_to);
583 free_recipients(valid_cc);
587 valid_bcc = validate_recipients(bcc, NULL, 0);
588 if (valid_bcc->num_error > 0) {
589 cprintf("%d %s\n", ERROR + NO_SUCH_USER, valid_bcc->errormsg);
590 free_recipients(valid_to);
591 free_recipients(valid_cc);
592 free_recipients(valid_bcc);
596 /* Recipient required, but none were specified */
597 if ( (valid_to->num_error < 0) && (valid_cc->num_error < 0) && (valid_bcc->num_error < 0) ) {
598 free_recipients(valid_to);
599 free_recipients(valid_cc);
600 free_recipients(valid_bcc);
601 cprintf("%d At least one recipient is required.\n", ERROR + NO_SUCH_USER);
605 if (valid_to->num_internet + valid_cc->num_internet + valid_bcc->num_internet > 0) {
606 if (CtdlCheckInternetMailPermission(&CCC->user)==0) {
607 cprintf("%d You do not have permission "
608 "to send Internet mail.\n",
609 ERROR + HIGHER_ACCESS_REQUIRED);
610 free_recipients(valid_to);
611 free_recipients(valid_cc);
612 free_recipients(valid_bcc);
617 if ( ( (valid_to->num_internet + valid_to->num_ignet + valid_cc->num_internet + valid_cc->num_ignet + valid_bcc->num_internet + valid_bcc->num_ignet) > 0)
618 && (CCC->user.axlevel < AxNetU) ) {
619 cprintf("%d Higher access required for network mail.\n",
620 ERROR + HIGHER_ACCESS_REQUIRED);
621 free_recipients(valid_to);
622 free_recipients(valid_cc);
623 free_recipients(valid_bcc);
627 if ((RESTRICT_INTERNET == 1)
628 && (valid_to->num_internet + valid_cc->num_internet + valid_bcc->num_internet > 0)
629 && ((CCC->user.flags & US_INTERNET) == 0)
630 && (!CCC->internal_pgm)) {
631 cprintf("%d You don't have access to Internet mail.\n",
632 ERROR + HIGHER_ACCESS_REQUIRED);
633 free_recipients(valid_to);
634 free_recipients(valid_cc);
635 free_recipients(valid_bcc);
641 /* Is this a room which has anonymous-only or anonymous-option? */
642 anonymous = MES_NORMAL;
643 if (CCC->room.QRflags & QR_ANONONLY) {
644 anonymous = MES_ANONONLY;
646 if (CCC->room.QRflags & QR_ANONOPT) {
647 if (anon_flag == 1) { /* only if the user requested it */
648 anonymous = MES_ANONOPT;
652 if ((CCC->room.QRflags & QR_MAILBOX) == 0) {
656 /* Recommend to the client that the use of a message subject is
657 * strongly recommended in this room, if either the SUBJECTREQ flag
658 * is set, or if there is one or more Internet email recipients.
660 if (CCC->room.QRflags2 & QR2_SUBJECTREQ) subject_required = 1;
661 if ((valid_to) && (valid_to->num_internet > 0)) subject_required = 1;
662 if ((valid_cc) && (valid_cc->num_internet > 0)) subject_required = 1;
663 if ((valid_bcc) && (valid_bcc->num_internet > 0)) subject_required = 1;
665 /* If we're only checking the validity of the request, return
666 * success without creating the message.
669 cprintf("%d %s|%d\n", CIT_OK,
670 ((valid_to != NULL) ? valid_to->display_recp : ""),
672 free_recipients(valid_to);
673 free_recipients(valid_cc);
674 free_recipients(valid_bcc);
678 /* We don't need these anymore because we'll do it differently below */
679 free_recipients(valid_to);
680 free_recipients(valid_cc);
681 free_recipients(valid_bcc);
683 /* Read in the message from the client. */
685 cprintf("%d send message\n", START_CHAT_MODE);
687 cprintf("%d send message\n", SEND_LISTING);
690 msg = CtdlMakeMessage(&CCC->user, recp, cc,
691 CCC->room.QRname, anonymous, format_type,
692 newusername, newuseremail, subject,
693 ((!IsEmptyStr(supplied_euid)) ? supplied_euid : NULL),
696 /* Put together one big recipients struct containing to/cc/bcc all in
697 * one. This is for the envelope.
699 char *all_recps = malloc(SIZ * 3);
700 strcpy(all_recps, recp);
701 if (!IsEmptyStr(cc)) {
702 if (!IsEmptyStr(all_recps)) {
703 strcat(all_recps, ",");
705 strcat(all_recps, cc);
707 if (!IsEmptyStr(bcc)) {
708 if (!IsEmptyStr(all_recps)) {
709 strcat(all_recps, ",");
711 strcat(all_recps, bcc);
713 if (!IsEmptyStr(all_recps)) {
714 valid = validate_recipients(all_recps, NULL, 0);
721 if ((valid != NULL) && (valid->num_room == 1) && !IsEmptyStr(valid->recp_orgroom))
723 /* posting into an ML room? set the envelope from
724 * to the actual mail address so others get a valid
727 CM_SetField(msg, eenVelopeTo, valid->recp_orgroom, strlen(valid->recp_orgroom));
731 msgnum = CtdlSubmitMsg(msg, valid, "", QP_EADDR);
733 cprintf("%ld\n", msgnum);
735 if (StrLength(CCC->StatusMessage) > 0) {
736 cprintf("%s\n", ChrPtr(CCC->StatusMessage));
738 else if (msgnum >= 0L) {
739 client_write(HKEY("Message accepted.\n"));
742 client_write(HKEY("Internal error.\n"));
745 if (!CM_IsEmpty(msg, eExclusiveID)) {
746 cprintf("%s\n", msg->cm_fields[eExclusiveID]);
756 free_recipients(valid);
762 * Delete message from current room
764 void cmd_dele(char *args)
773 extract_token(msgset, args, 0, '|', sizeof msgset);
774 num_msgs = num_tokens(msgset, ',');
776 cprintf("%d Nothing to do.\n", CIT_OK);
780 if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom() == 0) {
781 cprintf("%d Higher access required.\n",
782 ERROR + HIGHER_ACCESS_REQUIRED);
787 * Build our message set to be moved/copied
789 msgs = malloc(num_msgs * sizeof(long));
790 for (i=0; i<num_msgs; ++i) {
791 extract_token(msgtok, msgset, i, ',', sizeof msgtok);
792 msgs[i] = atol(msgtok);
795 num_deleted = CtdlDeleteMessages(CC->room.QRname, msgs, num_msgs, "");
799 cprintf("%d %d message%s deleted.\n", CIT_OK,
800 num_deleted, ((num_deleted != 1) ? "s" : ""));
802 cprintf("%d Message not found.\n", ERROR + MESSAGE_NOT_FOUND);
809 * move or copy a message to another room
811 void cmd_move(char *args)
818 char targ[ROOMNAMELEN];
819 struct ctdlroom qtemp;
826 extract_token(msgset, args, 0, '|', sizeof msgset);
827 num_msgs = num_tokens(msgset, ',');
829 cprintf("%d Nothing to do.\n", CIT_OK);
833 extract_token(targ, args, 1, '|', sizeof targ);
834 convert_room_name_macros(targ, sizeof targ);
835 targ[ROOMNAMELEN - 1] = 0;
836 is_copy = extract_int(args, 2);
838 if (CtdlGetRoom(&qtemp, targ) != 0) {
839 cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, targ);
843 if (!strcasecmp(qtemp.QRname, CC->room.QRname)) {
844 cprintf("%d Source and target rooms are the same.\n", ERROR + ALREADY_EXISTS);
848 CtdlGetUser(&CC->user, CC->curr_user);
849 CtdlRoomAccess(&qtemp, &CC->user, &ra, NULL);
851 /* Check for permission to perform this operation.
852 * Remember: "CC->room" is source, "qtemp" is target.
856 /* Admins can move/copy */
857 if (CC->user.axlevel >= AxAideU) permit = 1;
859 /* Room aides can move/copy */
860 if (CC->user.usernum == CC->room.QRroomaide) permit = 1;
862 /* Permit move/copy from personal rooms */
863 if ((CC->room.QRflags & QR_MAILBOX)
864 && (qtemp.QRflags & QR_MAILBOX)) permit = 1;
866 /* Permit only copy from public to personal room */
868 && (!(CC->room.QRflags & QR_MAILBOX))
869 && (qtemp.QRflags & QR_MAILBOX)) permit = 1;
871 /* Permit message removal from collaborative delete rooms */
872 if (CC->room.QRflags2 & QR2_COLLABDEL) permit = 1;
874 /* Users allowed to post into the target room may move into it too. */
875 if ((CC->room.QRflags & QR_MAILBOX) &&
876 (qtemp.QRflags & UA_POSTALLOWED)) permit = 1;
878 /* User must have access to target room */
879 if (!(ra & UA_KNOWN)) permit = 0;
882 cprintf("%d Higher access required.\n",
883 ERROR + HIGHER_ACCESS_REQUIRED);
888 * Build our message set to be moved/copied
890 msgs = malloc(num_msgs * sizeof(long));
891 for (i=0; i<num_msgs; ++i) {
892 extract_token(msgtok, msgset, i, ',', sizeof msgtok);
893 msgs[i] = atol(msgtok);
899 err = CtdlSaveMsgPointersInRoom(targ, msgs, num_msgs, 1, NULL, 0);
901 cprintf("%d Cannot store message(s) in %s: error %d\n",
907 /* Now delete the message from the source room,
908 * if this is a 'move' rather than a 'copy' operation.
911 CtdlDeleteMessages(CC->room.QRname, msgs, num_msgs, "");
915 cprintf("%d Message(s) %s.\n", CIT_OK, (is_copy ? "copied" : "moved") );
919 /*****************************************************************************/
920 /* MODULE INITIALIZATION STUFF */
921 /*****************************************************************************/
922 CTDL_MODULE_INIT(ctdl_message)
926 CtdlRegisterProtoHook(cmd_msgs, "MSGS", "Output a list of messages in the current room");
927 CtdlRegisterProtoHook(cmd_msg0, "MSG0", "Output a message in plain text format");
928 CtdlRegisterProtoHook(cmd_msg2, "MSG2", "Output a message in RFC822 format");
929 CtdlRegisterProtoHook(cmd_msg3, "MSG3", "Output a message in raw format (deprecated)");
930 CtdlRegisterProtoHook(cmd_msg4, "MSG4", "Output a message in the client's preferred format");
931 CtdlRegisterProtoHook(cmd_msgp, "MSGP", "Select preferred format for MSG4 output");
932 CtdlRegisterProtoHook(cmd_opna, "OPNA", "Open an attachment for download");
933 CtdlRegisterProtoHook(cmd_dlat, "DLAT", "Download an attachment");
934 CtdlRegisterProtoHook(cmd_ent0, "ENT0", "Enter a message");
935 CtdlRegisterProtoHook(cmd_dele, "DELE", "Delete a message");
936 CtdlRegisterProtoHook(cmd_move, "MOVE", "Move or copy a message to another room");
939 /* return our Subversion id for the Log */
940 return "ctdl_message";