}
-// These variables are used by both main() and ctdlmigrate_exit()
-// They are global so that ctdlmigrate_exit can be called from a signal handler
-FILE *sourcefp = NULL;
-char socket_path[PATH_MAX];
-pid_t sshpid = (-1);
-
void ctdlmigrate_exit(int cmdexit) {
- if (sourcefp) {
- printf("Closing the data connection from the source system...\n");
- pclose(sourcefp);
- }
- unlink(socket_path);
- if (sshpid > 0) {
- printf("Shutting down the SSH session...\n");
- kill(sshpid, SIGKILL);
- }
printf("\n\n\033[3%dmExit code %d\033[0m\n", (cmdexit ? 1 : 2), cmdexit);
exit(cmdexit);
}
+/*
+ * Connect to a Citadel on a remote host using a TCP/IP socket
+ */
+static int tcp_connectsock(char *host, char *service) {
+ struct in6_addr serveraddr;
+ struct addrinfo hints;
+ struct addrinfo *res = NULL;
+ struct addrinfo *ai = NULL;
+ int rc = (-1);
+ int sock = (-1);
+
+ if ((host == NULL) || IsEmptyStr(host)) {
+ return(-1);
+ }
+ if ((service == NULL) || IsEmptyStr(service)) {
+ return(-1);
+ }
+
+ memset(&hints, 0x00, sizeof(hints));
+ hints.ai_flags = AI_NUMERICSERV;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ /*
+ * Handle numeric IPv4 and IPv6 addresses
+ */
+ rc = inet_pton(AF_INET, host, &serveraddr);
+ if (rc == 1) { /* dotted quad */
+ hints.ai_family = AF_INET;
+ hints.ai_flags |= AI_NUMERICHOST;
+ } else {
+ rc = inet_pton(AF_INET6, host, &serveraddr);
+ if (rc == 1) { /* IPv6 address */
+ hints.ai_family = AF_INET6;
+ hints.ai_flags |= AI_NUMERICHOST;
+ }
+ }
+
+ /* Begin the connection process */
+
+ rc = getaddrinfo(host, service, &hints, &res);
+ if (rc != 0) {
+ fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * Try all available addresses until we connect to one or until we run out.
+ */
+ for (ai = res; ai != NULL; ai = ai->ai_next) {
+ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (sock < 0) {
+ fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
+ return (-1);
+ }
+
+ rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
+ if (rc >= 0) {
+ return (sock); /* Connected! */
+ }
+ else {
+ fprintf(stderr, "ctdlmigrate: %s\n", strerror(errno));
+ close(sock); /* Failed. Close the socket to avoid fd leak! */
+ }
+ }
+ return (-1);
+}
+
+
int uds_connectsock(char *sockpath) {
int s;
struct sockaddr_un addr;
s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0) {
fprintf(stderr, "ctdlmigrate: Can't create socket: %s\n", strerror(errno));
- ctdlmigrate_exit(3);
+ return(-1);
}
if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
fprintf(stderr, "ctdlmigrate: can't connect: %s\n", strerror(errno));
close(s);
- ctdlmigrate_exit(3);
+ return(-1);
}
return s;
}
+/*
+ * serv_printf() Send formatted printable data to the server
+ */
+void serv_printf(int serv_sock, const char *format, ...) {
+ va_list arg_ptr;
+ char buf[1024];
+
+ va_start(arg_ptr, format);
+ if (vsnprintf(buf, sizeof buf, format, arg_ptr) == -1)
+ buf[sizeof buf - 2] = '\n';
+ serv_write(serv_sock, buf, strlen(buf));
+ va_end(arg_ptr);
+}
+
+
int main(int argc, char *argv[]) {
char ctdldir[PATH_MAX]=CTDLDIR;
- char yesno[5];
+ char yesno[2];
int cmdexit = 0; // when something fails, set cmdexit to nonzero, and skip to the end
- char cmd[PATH_MAX];
char buf[PATH_MAX];
- char remote_user[SIZ];
- char remote_host[SIZ];
- char remote_sendcommand[PATH_MAX];
+
+ char remote_user[128];
+ char remote_host[128];
+ char remote_pass[128];
+ char *remote_port = "504";
+
int linecount = 0;
int a;
+ int remote_server_socket = (-1);
int local_admin_socket = (-1);
int progress = 0;
}
if (!cmdexit) {
-
printf( "\033[2J\033[H\n"
" \033[32m╔═══════════════════════════════════════════════╗\n"
" ║ ║\n"
printf("First we must validate that the local target system is running and ready to receive data.\n");
printf("Checking connectivity to Citadel in %s...\n", ctdldir);
local_admin_socket = uds_connectsock("citadel-admin.socket");
+ if (local_admin_socket < 0) {
+ cmdexit = 1;
+ }
+ }
+ if (!cmdexit) {
serv_gets(local_admin_socket, buf);
puts(buf);
if (buf[0] != '2') {
if (!cmdexit) {
printf("\n");
printf("OK, this side is ready to go. Now we must connect to the source system.\n\n");
- printf("Enter the host name or IP address of the source system\n");
- printf("(example: ctdl.foo.org)\n");
- getz(remote_host, sizeof remote_host, NULL, "--> ");
-
- while (IsEmptyStr(remote_user)) {
- printf("\n");
- printf("Enter the name of a user on %s who has full access to Citadel files.\n", remote_host);
- getz(remote_user, sizeof remote_user, "root", "--> ");
- }
- printf("\n");
- printf("Establishing an SSH connection to the source system...\n");
- sprintf(socket_path, "/tmp/ctdlmigrate-socket.%ld.%d", time(NULL), getpid());
- unlink(socket_path);
-
- snprintf(cmd, sizeof cmd, "ssh -MNf -o ServerAliveInterval=5 -S %s -l %s %s", socket_path, remote_user, remote_host);
- sshpid = fork();
- if (sshpid < 0) {
- printf("%s\n", strerror(errno));
- cmdexit = errno;
- }
- else if (sshpid == 0) {
- execl("/bin/bash", "bash", "-c", cmd, (char *) NULL);
- exit(1);
- }
- else { // Wait for SSH to go into the background
- waitpid(sshpid, &cmdexit, 0);
+ getz(remote_host, sizeof remote_host, NULL, "Enter the name or IP address of the source system: ");
+ getz(remote_user, sizeof remote_user, "admin", " Enter the user name of an administrator account: ");
+ getz(remote_pass, sizeof remote_pass, NULL, " Enter the password for this account: ");
+
+ remote_server_socket = tcp_connectsock(remote_host, remote_port);
+ if (remote_server_socket < 0) {
+ cmdexit = 1;
}
}
if (!cmdexit) {
- printf("\nTesting a command over the connection...\n\n");
- snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
- socket_path, remote_user, remote_host);
- cmdexit = system(cmd);
- printf("\n");
- if (cmdexit != 0) {
- printf("\033[31mRemote commands are not succeeding.\033[0m\n\n");
+ serv_gets(remote_server_socket, buf);
+ puts(buf);
+ if (buf[0] != '2') {
+ cmdexit = 1;
}
}
if (!cmdexit) {
- printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
- snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
- snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP", socket_path, remote_user, remote_host, remote_sendcommand);
- cmdexit = system(cmd);
-
- if (cmdexit) {
- snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
- snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP", socket_path, remote_user, remote_host, remote_sendcommand);
- cmdexit = system(cmd);
+ serv_printf(remote_server_socket, "USER %s\n", remote_user);
+ serv_gets(remote_server_socket, buf);
+ puts(buf);
+ if (buf[0] != '3') {
+ cmdexit = 1;
}
+ }
- if (cmdexit) {
- printf("\n");
- printf("Unable to locate Citadel programs on the remote system. Please enter\n"
- "the name of the directory on %s which contains the 'sendcommand' program.\n"
- "(example: /opt/foo/citadel)\n", remote_host);
- getz(remote_host, sizeof remote_host, "/usr/local/citadel", "--> ");
- snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
- snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP", socket_path, remote_user, remote_host, remote_sendcommand);
- cmdexit = system(cmd);
- if (!cmdexit) {
- printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
- }
+ if (!cmdexit) {
+ serv_printf(remote_server_socket, "PASS %s\n", remote_pass);
+ serv_gets(remote_server_socket, buf);
+ puts(buf);
+ if (buf[0] != '2') {
+ cmdexit = 1;
}
}
" ╚═══════════════════════════════════════════════════════════════════╝\033[0m\n"
"\n", remote_host
);
+ }
- snprintf(cmd, sizeof cmd, "ssh -o ServerAliveInterval=5 -S %s %s@%s %s -w3600 MIGR export", socket_path, remote_user, remote_host, remote_sendcommand);
- sourcefp = popen(cmd, "r");
- if (!sourcefp) {
- cmdexit = errno;
- printf("\n%s\n\n", strerror(errno));
+ if (!cmdexit) {
+ serv_puts(remote_server_socket, "MIGR export");
+ serv_gets(remote_server_socket, buf);
+ if (buf[0] != '1') {
+ printf("\n\033[31m%s\033[0m\n", buf);
+ cmdexit = 3;
}
}
if (!cmdexit) {
char *ptr;
time_t last_update = time(NULL);
- while (ptr = fgets(buf, SIZ, sourcefp), (ptr != NULL)) {
+
+ while (serv_gets(remote_server_socket, buf), strcmp(buf, "000")) {
ptr = strchr(buf, '\n');
if (ptr) *ptr = 0; // remove the newline character
++linecount;
-/*
- * Implements the message store.
- *
- * Copyright (c) 1987-2021 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
+// Implements the message store.
+//
+// Copyright (c) 1987-2021 by the citadel.org team
+//
+// This program is open source software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 3.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
#include <stdlib.h>
struct addresses_to_be_filed *atbf = NULL;
-/*
- * These are the four-character field headers we use when outputting
- * messages in Citadel format (as opposed to RFC822 format).
- */
+// These are the four-character field headers we use when outputting
+// messages in Citadel format (as opposed to RFC822 format).
char *msgkeys[] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
HashList *msgKeyLookup = NULL;
-int GetFieldFromMnemonic(eMsgField *f, const char* c)
-{
+int GetFieldFromMnemonic(eMsgField *f, const char* c) {
void *v = NULL;
if (GetHash(msgKeyLookup, c, 4, &v)) {
*f = (eMsgField) v;
return 0;
}
-void FillMsgKeyLookupTable(void)
-{
+void FillMsgKeyLookupTable(void) {
long i;
msgKeyLookup = NewHash (1, FourHash);
}
-/*
- * Returns 1 if the supplied pointer points to a valid Citadel message.
- * If the pointer is NULL or the magic number check fails, returns 0.
- */
+// Returns 1 if the supplied pointer points to a valid Citadel message.
+// If the pointer is NULL or the magic number check fails, returns 0.
int CM_IsValidMsg(struct CtdlMessage *msg) {
if (msg == NULL) {
return 0;
msg->cm_lengths[i] = 0;
}
- msg->cm_magic = 0; /* just in case */
+ msg->cm_magic = 0; // just in case
}
-/*
- * 'Destructor' for struct CtdlMessage
- */
+// 'Destructor' for struct CtdlMessage
void CM_Free(struct CtdlMessage *msg) {
if (CM_IsValidMsg(msg) == 0) {
if (msg != NULL) free (msg);
}
-/* Determine if a given message matches the fields in a message template.
- * Return 0 for a successful match.
- */
+// Determine if a given message matches the fields in a message template.
+// Return 0 for a successful match.
int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) {
int i;
- /* If there aren't any fields in the template, all messages will
- * match.
- */
+ // If there aren't any fields in the template, all messages will match.
if (template == NULL) return(0);
- /* Null messages are bogus. */
+ // Null messages are bogus.
if (msg == NULL) return(1);
for (i='A'; i<='Z'; ++i) {
if (template->cm_fields[i] != NULL) {
if (msg->cm_fields[i] == NULL) {
- /* Considered equal if temmplate is empty string */
+ // Considered equal if temmplate is empty string
if (IsEmptyStr(template->cm_fields[i])) continue;
return 1;
}
}
-/*
- * Retrieve the "seen" message list for the current room.
- */
+// Retrieve the "seen" message list for the current room.
void CtdlGetSeen(char *buf, int which_set) {
visit vbuf;
- /* Learn about the user and room in question */
+ // Learn about the user and room in question
CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
if (which_set == ctdlsetseen_seen) {
}
-/*
- * Manipulate the "seen msgs" string (or other message set strings)
- */
+// Manipulate the "seen msgs" string (or other message set strings)
void CtdlSetSeen(long *target_msgnums, int num_target_msgnums,
int target_setting, int which_set,
struct ctdluser *which_user, struct ctdlroom *which_room) {
return NULL;
}
- /* Parse the three bytes that begin EVERY message on disk.
- * The first is always 0xFF, the on-disk magic number.
- * The second is the anonymous/public type byte.
- * The third is the format type byte (vari, fixed, or MIME).
- */
+ // Parse the three bytes that begin EVERY message on disk.
+ // The first is always 0xFF, the on-disk magic number.
+ // The second is the anonymous/public type byte.
+ // The third is the format type byte (vari, fixed, or MIME).
+ //
ch = *mptr++;
if (ch != 255) {
syslog(LOG_ERR, "msgbase: message %ld appears to be corrupted", msgnum);
memset(ret, 0, sizeof(struct CtdlMessage));
ret->cm_magic = CTDLMESSAGE_MAGIC;
- ret->cm_anon_type = *mptr++; /* Anon type byte */
- ret->cm_format_type = *mptr++; /* Format type byte */
+ ret->cm_anon_type = *mptr++; // Anon type byte
+ ret->cm_format_type = *mptr++; // Format type byte
- /*
- * The rest is zero or more arbitrary fields. Load them in.
- * We're done when we encounter either a zero-length field or
- * have just processed the 'M' (message text) field.
- */
+ // The rest is zero or more arbitrary fields. Load them in.
+ // We're done when we encounter either a zero-length field or
+ // have just processed the 'M' (message text) field.
+ //
do {
field_header = '\0';
long len;
- /* work around possibly buggy messages: */
- while (field_header == '\0') {
+ while (field_header == '\0') { // work around possibly buggy messages
if (mptr >= upper_bound) {
break;
}
CM_SetField(ret, which, mptr, len);
- mptr += len + 1; /* advance to next field */
+ mptr += len + 1; // advance to next field
} while ((mptr < upper_bound) && (field_header != 'M'));
-
return (ret);
}
-/*
- * Load a message from disk into memory.
- * This is used by CtdlOutputMsg() and other fetch functions.
- *
- * NOTE: Caller is responsible for freeing the returned CtdlMessage struct
- * using the CM_Free(); function.
- */
-struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body)
-{
+// Load a message from disk into memory.
+// This is used by CtdlOutputMsg() and other fetch functions.
+//
+// NOTE: Caller is responsible for freeing the returned CtdlMessage struct
+// using the CM_Free(); function.
+//
+struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body) {
struct cdbdata *dmsgtext;
struct CtdlMessage *ret = NULL;
syslog(LOG_DEBUG, "msgbase: CtdlFetchMessage(%ld, %d)", msgnum, with_body);
dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
if (dmsgtext == NULL) {
- syslog(LOG_ERR, "msgbase: CtdlFetchMessage(%ld, %d) Failed!", msgnum, with_body);
+ syslog(LOG_ERR, "msgbase: message #%ld was not found", msgnum);
return NULL;
}
return NULL;
}
- /* Always make sure there's something in the msg text field. If
- * it's NULL, the message text is most likely stored separately,
- * so go ahead and fetch that. Failing that, just set a dummy
- * body so other code doesn't barf.
- */
+ // Always make sure there's something in the msg text field. If
+ // it's NULL, the message text is most likely stored separately,
+ // so go ahead and fetch that. Failing that, just set a dummy
+ // body so other code doesn't barf.
+ //
if ( (CM_IsEmpty(ret, eMesageText)) && (with_body) ) {
dmsgtext = cdb_fetch(CDB_BIGMSGS, &msgnum, sizeof(long));
if (dmsgtext != NULL) {
}
-/*
- * Pre callback function for multipart/alternative
- *
- * NOTE: this differs from the standard behavior for a reason. Normally when
- * displaying multipart/alternative you want to show the _last_ usable
- * format in the message. Here we show the _first_ one, because it's
- * usually text/plain. Since this set of functions is designed for text
- * output to non-MIME-aware clients, this is the desired behavior.
- *
- */
+// Pre callback function for multipart/alternative
+//
+// NOTE: this differs from the standard behavior for a reason. Normally when
+// displaying multipart/alternative you want to show the _last_ usable
+// format in the message. Here we show the _first_ one, because it's
+// usually text/plain. Since this set of functions is designed for text
+// output to non-MIME-aware clients, this is the desired behavior.
+//
void fixed_output_pre(char *name, char *filename, char *partnum, char *disp,
void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
char *cbid, void *cbuserdata)
}
-/*
- * Post callback function for multipart/alternative
- */
+//
+// Post callback function for multipart/alternative
+//
void fixed_output_post(char *name, char *filename, char *partnum, char *disp,
void *content, char *cbtype, char *cbcharset, size_t length,
char *encoding, char *cbid, void *cbuserdata)
}
-/*
- * Inline callback function for mime parser that wants to display text
- */
+// Inline callback function for mime parser that wants to display text
+//
void fixed_output(char *name, char *filename, char *partnum, char *disp,
void *content, char *cbtype, char *cbcharset, size_t length,
char *encoding, char *cbid, void *cbuserdata)
partnum, filename, cbtype, (long)length
);
- /*
- * If we're in the middle of a multipart/alternative scope and
- * we've already printed another section, skip this one.
- */
+ // If we're in the middle of a multipart/alternative scope and
+ // we've already printed another section, skip this one.
+ //
if ( (ma->is_ma) && (ma->did_print) ) {
syslog(LOG_DEBUG, "msgbase: skipping part %s (%s)", partnum, cbtype);
return;
}
if (ma->use_fo_hooks) {
- if (PerformFixedOutputHooks(cbtype, content, length)) {
- /* above function returns nonzero if it handled the part */
+ if (PerformFixedOutputHooks(cbtype, content, length)) { // returns nonzero if it handled the part
return;
}
}
}
-/*
- * The client is elegant and sophisticated and wants to be choosy about
- * MIME content types, so figure out which multipart/alternative part
- * we're going to send.
- *
- * We use a system of weights. When we find a part that matches one of the
- * MIME types we've declared as preferential, we can store it in ma->chosen_part
- * and then set ma->chosen_pref to that MIME type's position in our preference
- * list. If we then hit another match, we only replace the first match if
- * the preference value is lower.
- */
+// The client is elegant and sophisticated and wants to be choosy about
+// MIME content types, so figure out which multipart/alternative part
+// we're going to send.
+//
+// We use a system of weights. When we find a part that matches one of the
+// MIME types we've declared as preferential, we can store it in ma->chosen_part
+// and then set ma->chosen_pref to that MIME type's position in our preference
+// list. If we then hit another match, we only replace the first match if
+// the preference value is lower.
+//
void choose_preferred(char *name, char *filename, char *partnum, char *disp,
void *content, char *cbtype, char *cbcharset, size_t length,
char *encoding, char *cbid, void *cbuserdata)
}
-/*
- * Now that we've chosen our preferred part, output it.
- */
+// Now that we've chosen our preferred part, output it.
+//
void output_preferred(char *name,
char *filename,
char *partnum,
ma = (struct ma_info *)cbuserdata;
- /* This is not the MIME part you're looking for... */
+ // This is not the MIME part you're looking for...
if (strcasecmp(partnum, ma->chosen_part)) return;
- /* If the content-type of this part is in our preferred formats
- * list, we can simply output it verbatim.
- */
+ // If the content-type of this part is in our preferred formats
+ // list, we can simply output it verbatim.
for (i=0; i<num_tokens(CC->preferred_formats, '|'); ++i) {
extract_token(buf, CC->preferred_formats, i, '|', sizeof buf);
if (!strcasecmp(buf, cbtype)) {
&decoded,
&bytes_decoded);
if (rc < 0)
- break; /* Give us the chance, maybe theres another one. */
+ break; // Give us the chance, maybe theres another one.
if (rc == 0) text_content = (char *)content;
else {
}
}
- /* No translations required or possible: output as text/plain */
+ // No translations required or possible: output as text/plain
cprintf("Content-type: text/plain\n\n");
rc = 0;
if (ma->dont_decode == 0)
&decoded,
&bytes_decoded);
if (rc < 0)
- return; /* Give us the chance, maybe theres another one. */
+ return; // Give us the chance, maybe theres another one.
if (rc == 0) text_content = (char *)content;
else {
length = bytes_decoded;
}
- fixed_output(name, filename, partnum, disp, text_content, cbtype, cbcharset,
- length, encoding, cbid, cbuserdata);
+ fixed_output(name, filename, partnum, disp, text_content, cbtype, cbcharset, length, encoding, cbid, cbuserdata);
if (decoded != NULL) free(decoded);
}
};
-/*
- * Callback function for
- */
+// Callback function
void extract_encapsulated_message(char *name, char *filename, char *partnum, char *disp,
void *content, char *cbtype, char *cbcharset, size_t length,
char *encoding, char *cbid, void *cbuserdata)
encap = (struct encapmsg *)cbuserdata;
- /* Only proceed if this is the desired section... */
+ // Only proceed if this is the desired section...
if (!strcasecmp(encap->desired_section, partnum)) {
encap->msglen = length;
encap->msg = malloc(length + 2);
}
-/*
- * Determine whether the specified message exists in the cached_msglist
- * (This is a security check)
- */
+// Determine whether the specified message exists in the cached_msglist
+// (This is a security check)
int check_cached_msglist(long msgnum) {
- /* cases in which we skip the check */
- if (!CC) return om_ok; /* not a session */
- if (CC->client_socket <= 0) return om_ok; /* not a client session */
- if (CC->cached_msglist == NULL) return om_access_denied; /* no msglist fetched */
- if (CC->cached_num_msgs == 0) return om_access_denied; /* nothing to check */
-
+ // cases in which we skip the check
+ if (!CC) return om_ok; // not a session
+ if (CC->client_socket <= 0) return om_ok; // not a client session
+ if (CC->cached_msglist == NULL) return om_access_denied; // no msglist fetched
+ if (CC->cached_num_msgs == 0) return om_access_denied; // nothing to check
- /* Do a binary search within the cached_msglist for the requested msgnum */
+ // Do a binary search within the cached_msglist for the requested msgnum
int min = 0;
int max = (CC->cached_num_msgs - 1);
}
-/*
- * Get a message off disk. (returns om_* values found in msgbase.h)
- *
- */
-int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */
- int mode, /* how would you like that message? */
- int headers_only, /* eschew the message body? */
- int do_proto, /* do Citadel protocol responses? */
- int crlf, /* Use CRLF newlines instead of LF? */
- char *section, /* NULL or a message/rfc822 section */
- int flags, /* various flags; see msgbase.h */
+// Get a message off disk. (returns om_* values found in msgbase.h)
+int CtdlOutputMsg(long msg_num, // message number (local) to fetch
+ int mode, // how would you like that message?
+ int headers_only, // eschew the message body?
+ int do_proto, // do Citadel protocol responses?
+ int crlf, // Use CRLF newlines instead of LF?
+ char *section, // NULL or a message/rfc822 section
+ int flags, // various flags; see msgbase.h
char **Author,
char **Address,
char **MessageID