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++) {
61 // output all fields except the references hash
62 cprintf("%ld|%s|%s|%s|%s|%s",
64 (!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0"),
65 (!CM_IsEmpty(msg, eAuthor) ? msg->cm_fields[eAuthor] : ""),
66 (!CM_IsEmpty(msg, eNodeName) ? msg->cm_fields[eNodeName] : ""),
67 (!CM_IsEmpty(msg, erFc822Addr) ? msg->cm_fields[erFc822Addr] : ""),
68 (!CM_IsEmpty(msg, eMsgSubject) ? msg->cm_fields[eMsgSubject] : "")
71 if (output_mode == MSG_HDRS_THREADS) { // field view with thread hashes
73 // output the references hash
75 (!CM_IsEmpty(msg, emessageId) ? HashLittle(msg->cm_fields[emessageId],strlen(msg->cm_fields[emessageId])) : 0)
78 // output the references hash (yes it's ok that we're trashing the source buffer by doing this)
79 if (!CM_IsEmpty(msg, eWeferences)) {
81 char *rest = msg->cm_fields[eWeferences];
83 while((token = strtok_r(rest, "|", &rest))) {
84 cprintf("%d%s", HashLittle(token,rest-prev-(*rest==0?0:1)), (*rest==0?"":","));
92 else { // field view with no threads, subject extends out forever
99 typedef struct _msg_filter{
105 void headers_brief_filter(long msgnum, void *userdata)
108 struct CtdlMessage *msg;
109 msg_filter *flt = (msg_filter*) userdata;
111 l = GetCount(flt->Filter);
112 msg = CtdlFetchMessage(msgnum, 0, 1);
113 StrBufPrintf(flt->buffer, "%ld", msgnum);
115 for (i = 0; i < l; i++) {
116 StrBufAppendBufPlain(flt->buffer, HKEY("|"), 0);
123 RewindHashPos(flt->Filter, flt->p, 0);
124 while (GetNextHashPos(flt->Filter, flt->p, &len, &k, &v)) {
125 eMsgField f = (eMsgField) v;
127 StrBufAppendBufPlain(flt->buffer, HKEY("|"), 0);
128 if (!CM_IsEmpty(msg, f)) {
129 StrBufAppendBufPlain(flt->buffer, CM_KEY(msg, f), 0);
133 StrBufAppendBufPlain(flt->buffer, HKEY("\n"), 0);
134 cputbuf(flt->buffer);
138 * Back end for the MSGS command: output EUID header.
140 void headers_euid(long msgnum, void *userdata)
142 struct CtdlMessage *msg;
144 msg = CtdlFetchMessage(msgnum, 0, 1);
146 cprintf("%ld||\n", msgnum);
150 cprintf("%ld|%s|%s\n",
152 (!CM_IsEmpty(msg, eExclusiveID) ? msg->cm_fields[eExclusiveID] : ""),
153 (!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0"));
160 * cmd_msgs() - get list of message #'s in this room
161 * implements the MSGS server command using CtdlForEachMessage()
163 void cmd_msgs(char *cmdbuf)
171 int with_template = 0;
172 struct CtdlMessage *template = NULL;
174 char search_string[1024];
175 ForEachMsgCallback CallBack;
177 if (CtdlAccessCheck(ac_logged_in_or_guest)) return;
179 extract_token(which, cmdbuf, 0, '|', sizeof which);
180 cm_ref = extract_int(cmdbuf, 1);
181 extract_token(search_string, cmdbuf, 1, '|', sizeof search_string);
182 with_template = extract_int(cmdbuf, 2);
183 int output_mode = extract_int(cmdbuf, 3);
184 switch (output_mode) {
187 CallBack = simple_listing;
190 case MSG_HDRS_THREADS:
191 CallBack = headers_listing;
194 CallBack = headers_euid;
196 case MSG_HDRS_BRIEFFILTER:
198 CallBack = headers_brief_filter;
203 if (!strncasecmp(which, "OLD", 3))
205 else if (!strncasecmp(which, "NEW", 3))
207 else if (!strncasecmp(which, "FIRST", 5))
209 else if (!strncasecmp(which, "LAST", 4))
211 else if (!strncasecmp(which, "GT", 2))
213 else if (!strncasecmp(which, "LT", 2))
215 else if (!strncasecmp(which, "SEARCH", 6))
220 if ( (mode == MSGS_SEARCH) && (!CtdlGetConfigInt("c_enable_fulltext")) ) {
221 cprintf("%d Full text index is not enabled on this server.\n",
222 ERROR + CMD_NOT_SUPPORTED);
226 if (with_template == 1) {
229 cprintf("%d Send template then receive message list\n",
231 template = (struct CtdlMessage *)
232 malloc(sizeof(struct CtdlMessage));
233 memset(template, 0, sizeof(struct CtdlMessage));
234 template->cm_magic = CTDLMESSAGE_MAGIC;
235 template->cm_anon_type = MES_NORMAL;
237 while(client_getln(buf, sizeof buf) >= 0 && strcmp(buf,"000")) {
241 tValueLen = extract_token(tfield, buf, 0, '|', sizeof tfield);
242 if ((tValueLen == 4) && GetFieldFromMnemonic(&f, tfield))
244 tValueLen = extract_token(tvalue, buf, 1, '|', sizeof tvalue);
245 if (tValueLen >= 0) {
246 CM_SetField(template, f, tvalue, tValueLen);
252 else if (with_template == 2) {
255 cprintf("%d Send list of headers\n",
257 filt.Filter = NewHash(1, lFlathash);
258 filt.buffer = NewStrBufPlain(NULL, 1024);
259 while(client_getln(buf, sizeof buf) >= 0 && strcmp(buf,"000")) {
262 if (GetFieldFromMnemonic(&f, buf))
264 Put(filt.Filter, LKEY(i), (void*)f, reference_free_handler);
268 filt.p = GetNewHashPos(filt.Filter, 0);
272 cprintf("%d \n", LISTING_FOLLOWS);
275 if (with_template < 2) {
276 CtdlForEachMessage(mode,
277 ( (mode == MSGS_SEARCH) ? 0 : cm_ref ),
278 ( (mode == MSGS_SEARCH) ? search_string : NULL ),
283 if (template != NULL) CM_Free(template);
286 CtdlForEachMessage(mode,
287 ( (mode == MSGS_SEARCH) ? 0 : cm_ref ),
288 ( (mode == MSGS_SEARCH) ? search_string : NULL ),
293 DeleteHashPos(&filt.p);
294 DeleteHash(&filt.Filter);
295 FreeStrBuf(&filt.buffer);
302 * display a message (mode 0 - Citadel proprietary)
304 void cmd_msg0(char *cmdbuf)
307 int headers_only = HEADERS_ALL;
309 msgid = extract_long(cmdbuf, 0);
310 headers_only = extract_int(cmdbuf, 1);
312 CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0, NULL, 0, NULL, NULL, NULL);
318 * display a message (mode 2 - RFC822)
320 void cmd_msg2(char *cmdbuf)
323 int headers_only = HEADERS_ALL;
325 msgid = extract_long(cmdbuf, 0);
326 headers_only = extract_int(cmdbuf, 1);
328 CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1, NULL, 0, NULL, NULL, NULL);
334 * display a message (mode 3 - IGnet raw format - internal programs only)
336 void cmd_msg3(char *cmdbuf)
339 struct CtdlMessage *msg = NULL;
342 if (CC->internal_pgm == 0) {
343 cprintf("%d This command is for internal programs only.\n",
344 ERROR + HIGHER_ACCESS_REQUIRED);
348 msgnum = extract_long(cmdbuf, 0);
349 msg = CtdlFetchMessage(msgnum, 1, 1);
351 cprintf("%d Message %ld not found.\n",
352 ERROR + MESSAGE_NOT_FOUND, msgnum);
356 CtdlSerializeMessage(&smr, msg);
360 cprintf("%d Unable to serialize message\n",
361 ERROR + INTERNAL_ERROR);
365 cprintf("%d %ld\n", BINARY_FOLLOWS, (long)smr.len);
366 client_write((char *)smr.ser, (int)smr.len);
373 * Display a message using MIME content types
375 void cmd_msg4(char *cmdbuf)
380 msgid = extract_long(cmdbuf, 0);
381 extract_token(section, cmdbuf, 1, '|', sizeof section);
382 CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0, (section[0] ? section : NULL) , 0, NULL, NULL, NULL);
388 * Client tells us its preferred message format(s)
390 void cmd_msgp(char *cmdbuf)
392 if (!strcasecmp(cmdbuf, "dont_decode")) {
393 CC->msg4_dont_decode = 1;
394 cprintf("%d MSG4 will not pre-decode messages.\n", CIT_OK);
397 safestrncpy(CC->preferred_formats, cmdbuf, sizeof(CC->preferred_formats));
398 cprintf("%d Preferred MIME formats have been set.\n", CIT_OK);
404 * Open a component of a MIME message as a download file
406 void cmd_opna(char *cmdbuf)
409 char desired_section[128];
411 msgid = extract_long(cmdbuf, 0);
412 extract_token(desired_section, cmdbuf, 1, '|', sizeof desired_section);
413 safestrncpy(CC->download_desired_section, desired_section,
414 sizeof CC->download_desired_section);
415 CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1, NULL, 0, NULL, NULL, NULL);
420 * Open a component of a MIME message and transmit it all at once
422 void cmd_dlat(char *cmdbuf)
425 char desired_section[128];
427 msgid = extract_long(cmdbuf, 0);
428 extract_token(desired_section, cmdbuf, 1, '|', sizeof desired_section);
429 safestrncpy(CC->download_desired_section, desired_section,
430 sizeof CC->download_desired_section);
431 CtdlOutputMsg(msgid, MT_SPEW_SECTION, 0, 1, 1, NULL, 0, NULL, NULL, NULL);
435 * message entry - mode 0 (normal)
437 void cmd_ent0(char *entargs)
439 struct CitContext *CCC = CC;
444 char supplied_euid[128];
447 char newusername[256];
448 char newuseremail[256];
449 struct CtdlMessage *msg;
453 recptypes *valid = NULL;
454 recptypes *valid_to = NULL;
455 recptypes *valid_cc = NULL;
456 recptypes *valid_bcc = NULL;
458 int subject_required = 0;
463 int newuseremail_ok = 0;
464 char references[SIZ];
469 post = extract_int(entargs, 0);
470 extract_token(recp, entargs, 1, '|', sizeof recp);
471 anon_flag = extract_int(entargs, 2);
472 format_type = extract_int(entargs, 3);
473 extract_token(subject, entargs, 4, '|', sizeof subject);
474 extract_token(newusername, entargs, 5, '|', sizeof newusername);
475 do_confirm = extract_int(entargs, 6);
476 extract_token(cc, entargs, 7, '|', sizeof cc);
477 extract_token(bcc, entargs, 8, '|', sizeof bcc);
478 switch(CC->room.QRdefaultview) {
482 extract_token(supplied_euid, entargs, 9, '|', sizeof supplied_euid);
485 supplied_euid[0] = 0;
488 extract_token(newuseremail, entargs, 10, '|', sizeof newuseremail);
489 extract_token(references, entargs, 11, '|', sizeof references);
490 for (ptr=references; *ptr != 0; ++ptr) {
491 if (*ptr == '!') *ptr = '|';
494 /* first check to make sure the request is valid. */
496 err = CtdlDoIHavePermissionToPostInThisRoom(
501 (!IsEmptyStr(references)) /* is this a reply? or a top-level post? */
505 cprintf("%d %s\n", err, errmsg);
509 /* Check some other permission type things. */
511 if (IsEmptyStr(newusername)) {
512 strcpy(newusername, CCC->user.fullname);
514 if ( (CCC->user.axlevel < AxAideU)
515 && (strcasecmp(newusername, CCC->user.fullname))
516 && (strcasecmp(newusername, CCC->cs_inet_fn))
518 cprintf("%d You don't have permission to author messages as '%s'.\n",
519 ERROR + HIGHER_ACCESS_REQUIRED,
526 if (IsEmptyStr(newuseremail)) {
530 if (!IsEmptyStr(newuseremail)) {
531 if (!strcasecmp(newuseremail, CCC->cs_inet_email)) {
534 else if (!IsEmptyStr(CCC->cs_inet_other_emails)) {
535 j = num_tokens(CCC->cs_inet_other_emails, '|');
536 for (i=0; i<j; ++i) {
537 extract_token(buf, CCC->cs_inet_other_emails, i, '|', sizeof buf);
538 if (!strcasecmp(newuseremail, buf)) {
545 if (!newuseremail_ok) {
546 cprintf("%d You don't have permission to author messages as '%s'.\n",
547 ERROR + HIGHER_ACCESS_REQUIRED,
553 CCC->cs_flags |= CS_POSTING;
555 /* In mailbox rooms we have to behave a little differently --
556 * make sure the user has specified at least one recipient. Then
557 * validate the recipient(s). We do this for the Mail> room, as
558 * well as any room which has the "Mailbox" view set - unless it
559 * is the DRAFTS room which does not require recipients
562 if ( ( ( (CCC->room.QRflags & QR_MAILBOX) && (!strcasecmp(&CCC->room.QRname[11], MAILROOM)) )
563 || ( (CCC->room.QRflags & QR_MAILBOX) && (CCC->curr_view == VIEW_MAILBOX) )
564 ) && (strcasecmp(&CCC->room.QRname[11], USERDRAFTROOM)) !=0 ) {
565 if (CCC->user.axlevel < AxProbU) {
566 strcpy(recp, "sysop");
571 valid_to = validate_recipients(recp, NULL, 0);
572 if (valid_to->num_error > 0) {
573 cprintf("%d %s\n", ERROR + NO_SUCH_USER, valid_to->errormsg);
574 free_recipients(valid_to);
578 valid_cc = validate_recipients(cc, NULL, 0);
579 if (valid_cc->num_error > 0) {
580 cprintf("%d %s\n", ERROR + NO_SUCH_USER, valid_cc->errormsg);
581 free_recipients(valid_to);
582 free_recipients(valid_cc);
586 valid_bcc = validate_recipients(bcc, NULL, 0);
587 if (valid_bcc->num_error > 0) {
588 cprintf("%d %s\n", ERROR + NO_SUCH_USER, valid_bcc->errormsg);
589 free_recipients(valid_to);
590 free_recipients(valid_cc);
591 free_recipients(valid_bcc);
595 /* Recipient required, but none were specified */
596 if ( (valid_to->num_error < 0) && (valid_cc->num_error < 0) && (valid_bcc->num_error < 0) ) {
597 free_recipients(valid_to);
598 free_recipients(valid_cc);
599 free_recipients(valid_bcc);
600 cprintf("%d At least one recipient is required.\n", ERROR + NO_SUCH_USER);
604 if (valid_to->num_internet + valid_cc->num_internet + valid_bcc->num_internet > 0) {
605 if (CtdlCheckInternetMailPermission(&CCC->user)==0) {
606 cprintf("%d You do not have permission "
607 "to send Internet mail.\n",
608 ERROR + HIGHER_ACCESS_REQUIRED);
609 free_recipients(valid_to);
610 free_recipients(valid_cc);
611 free_recipients(valid_bcc);
616 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)
617 && (CCC->user.axlevel < AxNetU) ) {
618 cprintf("%d Higher access required for network mail.\n",
619 ERROR + HIGHER_ACCESS_REQUIRED);
620 free_recipients(valid_to);
621 free_recipients(valid_cc);
622 free_recipients(valid_bcc);
626 if ((RESTRICT_INTERNET == 1)
627 && (valid_to->num_internet + valid_cc->num_internet + valid_bcc->num_internet > 0)
628 && ((CCC->user.flags & US_INTERNET) == 0)
629 && (!CCC->internal_pgm)) {
630 cprintf("%d You don't have access to Internet mail.\n",
631 ERROR + HIGHER_ACCESS_REQUIRED);
632 free_recipients(valid_to);
633 free_recipients(valid_cc);
634 free_recipients(valid_bcc);
640 /* Is this a room which has anonymous-only or anonymous-option? */
641 anonymous = MES_NORMAL;
642 if (CCC->room.QRflags & QR_ANONONLY) {
643 anonymous = MES_ANONONLY;
645 if (CCC->room.QRflags & QR_ANONOPT) {
646 if (anon_flag == 1) { /* only if the user requested it */
647 anonymous = MES_ANONOPT;
651 if ((CCC->room.QRflags & QR_MAILBOX) == 0) {
655 /* Recommend to the client that the use of a message subject is
656 * strongly recommended in this room, if either the SUBJECTREQ flag
657 * is set, or if there is one or more Internet email recipients.
659 if (CCC->room.QRflags2 & QR2_SUBJECTREQ) subject_required = 1;
660 if ((valid_to) && (valid_to->num_internet > 0)) subject_required = 1;
661 if ((valid_cc) && (valid_cc->num_internet > 0)) subject_required = 1;
662 if ((valid_bcc) && (valid_bcc->num_internet > 0)) subject_required = 1;
664 /* If we're only checking the validity of the request, return
665 * success without creating the message.
668 cprintf("%d %s|%d\n", CIT_OK,
669 ((valid_to != NULL) ? valid_to->display_recp : ""),
671 free_recipients(valid_to);
672 free_recipients(valid_cc);
673 free_recipients(valid_bcc);
677 /* We don't need these anymore because we'll do it differently below */
678 free_recipients(valid_to);
679 free_recipients(valid_cc);
680 free_recipients(valid_bcc);
682 /* Read in the message from the client. */
684 cprintf("%d send message\n", START_CHAT_MODE);
686 cprintf("%d send message\n", SEND_LISTING);
689 msg = CtdlMakeMessage(&CCC->user, recp, cc,
690 CCC->room.QRname, anonymous, format_type,
691 newusername, newuseremail, subject,
692 ((!IsEmptyStr(supplied_euid)) ? supplied_euid : NULL),
695 /* Put together one big recipients struct containing to/cc/bcc all in
696 * one. This is for the envelope.
698 char *all_recps = malloc(SIZ * 3);
699 strcpy(all_recps, recp);
700 if (!IsEmptyStr(cc)) {
701 if (!IsEmptyStr(all_recps)) {
702 strcat(all_recps, ",");
704 strcat(all_recps, cc);
706 if (!IsEmptyStr(bcc)) {
707 if (!IsEmptyStr(all_recps)) {
708 strcat(all_recps, ",");
710 strcat(all_recps, bcc);
712 if (!IsEmptyStr(all_recps)) {
713 valid = validate_recipients(all_recps, NULL, 0);
720 if ((valid != NULL) && (valid->num_room == 1) && !IsEmptyStr(valid->recp_orgroom))
722 /* posting into an ML room? set the envelope from
723 * to the actual mail address so others get a valid
726 CM_SetField(msg, eenVelopeTo, valid->recp_orgroom, strlen(valid->recp_orgroom));
730 msgnum = CtdlSubmitMsg(msg, valid, "", QP_EADDR);
732 cprintf("%ld\n", msgnum);
734 if (StrLength(CCC->StatusMessage) > 0) {
735 cprintf("%s\n", ChrPtr(CCC->StatusMessage));
737 else if (msgnum >= 0L) {
738 client_write(HKEY("Message accepted.\n"));
741 client_write(HKEY("Internal error.\n"));
744 if (!CM_IsEmpty(msg, eExclusiveID)) {
745 cprintf("%s\n", msg->cm_fields[eExclusiveID]);
755 free_recipients(valid);
761 * Delete message from current room
763 void cmd_dele(char *args)
772 extract_token(msgset, args, 0, '|', sizeof msgset);
773 num_msgs = num_tokens(msgset, ',');
775 cprintf("%d Nothing to do.\n", CIT_OK);
779 if (CtdlDoIHavePermissionToDeleteMessagesFromThisRoom() == 0) {
780 cprintf("%d Higher access required.\n",
781 ERROR + HIGHER_ACCESS_REQUIRED);
786 * Build our message set to be moved/copied
788 msgs = malloc(num_msgs * sizeof(long));
789 for (i=0; i<num_msgs; ++i) {
790 extract_token(msgtok, msgset, i, ',', sizeof msgtok);
791 msgs[i] = atol(msgtok);
794 num_deleted = CtdlDeleteMessages(CC->room.QRname, msgs, num_msgs, "");
798 cprintf("%d %d message%s deleted.\n", CIT_OK,
799 num_deleted, ((num_deleted != 1) ? "s" : ""));
801 cprintf("%d Message not found.\n", ERROR + MESSAGE_NOT_FOUND);
808 * move or copy a message to another room
810 void cmd_move(char *args)
817 char targ[ROOMNAMELEN];
818 struct ctdlroom qtemp;
825 extract_token(msgset, args, 0, '|', sizeof msgset);
826 num_msgs = num_tokens(msgset, ',');
828 cprintf("%d Nothing to do.\n", CIT_OK);
832 extract_token(targ, args, 1, '|', sizeof targ);
833 convert_room_name_macros(targ, sizeof targ);
834 targ[ROOMNAMELEN - 1] = 0;
835 is_copy = extract_int(args, 2);
837 if (CtdlGetRoom(&qtemp, targ) != 0) {
838 cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, targ);
842 if (!strcasecmp(qtemp.QRname, CC->room.QRname)) {
843 cprintf("%d Source and target rooms are the same.\n", ERROR + ALREADY_EXISTS);
847 CtdlGetUser(&CC->user, CC->curr_user);
848 CtdlRoomAccess(&qtemp, &CC->user, &ra, NULL);
850 /* Check for permission to perform this operation.
851 * Remember: "CC->room" is source, "qtemp" is target.
855 /* Admins can move/copy */
856 if (CC->user.axlevel >= AxAideU) permit = 1;
858 /* Room aides can move/copy */
859 if (CC->user.usernum == CC->room.QRroomaide) permit = 1;
861 /* Permit move/copy from personal rooms */
862 if ((CC->room.QRflags & QR_MAILBOX)
863 && (qtemp.QRflags & QR_MAILBOX)) permit = 1;
865 /* Permit only copy from public to personal room */
867 && (!(CC->room.QRflags & QR_MAILBOX))
868 && (qtemp.QRflags & QR_MAILBOX)) permit = 1;
870 /* Permit message removal from collaborative delete rooms */
871 if (CC->room.QRflags2 & QR2_COLLABDEL) permit = 1;
873 /* Users allowed to post into the target room may move into it too. */
874 if ((CC->room.QRflags & QR_MAILBOX) &&
875 (qtemp.QRflags & UA_POSTALLOWED)) permit = 1;
877 /* User must have access to target room */
878 if (!(ra & UA_KNOWN)) permit = 0;
881 cprintf("%d Higher access required.\n",
882 ERROR + HIGHER_ACCESS_REQUIRED);
887 * Build our message set to be moved/copied
889 msgs = malloc(num_msgs * sizeof(long));
890 for (i=0; i<num_msgs; ++i) {
891 extract_token(msgtok, msgset, i, ',', sizeof msgtok);
892 msgs[i] = atol(msgtok);
898 err = CtdlSaveMsgPointersInRoom(targ, msgs, num_msgs, 1, NULL, 0);
900 cprintf("%d Cannot store message(s) in %s: error %d\n",
906 /* Now delete the message from the source room,
907 * if this is a 'move' rather than a 'copy' operation.
910 CtdlDeleteMessages(CC->room.QRname, msgs, num_msgs, "");
914 cprintf("%d Message(s) %s.\n", CIT_OK, (is_copy ? "copied" : "moved") );
918 /*****************************************************************************/
919 /* MODULE INITIALIZATION STUFF */
920 /*****************************************************************************/
921 CTDL_MODULE_INIT(ctdl_message)
925 CtdlRegisterProtoHook(cmd_msgs, "MSGS", "Output a list of messages in the current room");
926 CtdlRegisterProtoHook(cmd_msg0, "MSG0", "Output a message in plain text format");
927 CtdlRegisterProtoHook(cmd_msg2, "MSG2", "Output a message in RFC822 format");
928 CtdlRegisterProtoHook(cmd_msg3, "MSG3", "Output a message in raw format (deprecated)");
929 CtdlRegisterProtoHook(cmd_msg4, "MSG4", "Output a message in the client's preferred format");
930 CtdlRegisterProtoHook(cmd_msgp, "MSGP", "Select preferred format for MSG4 output");
931 CtdlRegisterProtoHook(cmd_opna, "OPNA", "Open an attachment for download");
932 CtdlRegisterProtoHook(cmd_dlat, "DLAT", "Download an attachment");
933 CtdlRegisterProtoHook(cmd_ent0, "ENT0", "Enter a message");
934 CtdlRegisterProtoHook(cmd_dele, "DELE", "Delete a message");
935 CtdlRegisterProtoHook(cmd_move, "MOVE", "Move or copy a message to another room");
938 /* return our Subversion id for the Log */
939 return "ctdl_message";