--- /dev/null
+# config.mk is generated by configure
+CTDLDIR := /root/citadel/citadel
+CFLAGS := -ggdb -DHAVE_OPENSSL -DBUILD_ID=\"v957-38-g71c3e52b0\" -DCTDLDIR=\"/root/citadel/citadel\"
+LDFLAGS := -lssl -lcrypto -lz
--- /dev/null
+HAVE_ARPA_NAMESER_H
+HAVE_CONNECT
+HAVE_DB_H
+HAVE_DIRENT_H
+HAVE_FCNTL_H
+HAVE_GETHOSTBYNAME
+HAVE_GETLOADAVG
+HAVE_GETTEXT
+HAVE_GETUTXLINE
+HAVE_INTTYPES_H
+HAVE_LIBPTHREAD
+HAVE_LIBRT
+HAVE_LIMITS_H
+HAVE_MALLOC_H
+HAVE_MKDIR
+HAVE_MKFIFO
+HAVE_MKTIME
+HAVE_NETINET_IN_H
+HAVE_PATHS_H
+HAVE_PTHREAD_CANCEL
+HAVE_RMDIR
+HAVE_SELECT
+HAVE_SOCKET
+HAVE_STDINT_H
+HAVE_STDLIB_H
+HAVE_STRCASECMP
+HAVE_STRERROR
+HAVE_STRFTIME_L
+HAVE_STRNCASECMP
+HAVE_SYSLOG_H
+HAVE_SYS_IOCTL_H
+HAVE_SYS_STAT_H
+HAVE_SYS_TIME_H
+HAVE_SYS_TYPES_H
+HAVE_SYS_WAIT_H
+HAVE_TERMIOS_H
+HAVE_UNISTD_H
+HAVE_USELOCALE
+HAVE_UTMPX_H
+HAVE_UTMP_H
+HAVE_UT_HOST
+HAVE_UT_TYPE
+HAVE_VPRINTF
--- /dev/null
+/*
+ * (c) 2009-2020 by Art Cancro and citadel.org
+ * This program is open source. It runs great on the Linux operating system.
+ * It's released under the General Public License (GPL) version 3.
+ */
+
+#include "ctdlsh.h"
+
+
+/*
+ * Commands understood by ctdlsh
+ */
+typedef struct {
+ char *name;
+ ctdlsh_cmdfunc_t *func;
+ char *doc;
+} COMMAND;
+
+COMMAND commands[] = {
+ {"?", cmd_help, "Display this message"},
+ {"help", cmd_help, "Display this message"},
+ {"date", cmd_datetime, "Print the server's date and time"},
+ {"config", cmd_config, "Configure the Citadel server"},
+ {"export", cmd_export, "Export all Citadel databases"},
+ {"shutdown", cmd_shutdown, "Shut down the Citadel server"},
+ {"time", cmd_datetime, "Print the server's date and time"},
+ {"passwd", cmd_passwd, "Set or change an account password"},
+ {"who", cmd_who, "Display a list of online users"},
+ {"mailq", cmd_mailq, "Show the outbound email queue"},
+ {NULL, NULL, NULL}
+};
+
+
+
+int cmd_help(int sock, char *cmdbuf)
+{
+ int i;
+
+ for (i = 0; commands[i].func != NULL; ++i) {
+ printf("%10s %s\n", commands[i].name, commands[i].doc);
+ }
+}
+
+
+int do_one_command(int server_socket, char *cmd)
+{
+ int i;
+ int ret;
+ for (i = 0; commands[i].func != NULL; ++i) {
+ if (!strncasecmp(cmd, commands[i].name, strlen(commands[i].name))) {
+ ret = (*commands[i].func) (server_socket, cmd);
+ }
+ }
+ return ret;
+}
+
+char *command_name_generator(const char *text, int state)
+{
+ static int list_index, len;
+ char *name;
+
+ if (!state) {
+ list_index = 0;
+ len = strlen(text);
+ }
+
+ while (name = commands[list_index++].name) {
+ if (strncmp(name, text, len) == 0) {
+ return strdup(name);
+ }
+ }
+
+ return NULL;
+}
+
+
+char **command_name_completion(const char *text, int start, int end)
+{
+ rl_attempted_completion_over = 1;
+ return rl_completion_matches(text, command_name_generator);
+}
+
+
+void do_main_loop(int server_socket)
+{
+ char *cmd = NULL;
+ char prompt[1024];
+ char buf[1024];
+ char server_reply[1024];
+ int i;
+ int ret = (-1);
+
+ strcpy(prompt, "> ");
+
+ /* Do an INFO command and learn the hostname for the prompt */
+ sock_puts(server_socket, "INFO");
+ sock_getln(server_socket, buf, sizeof buf);
+ if (buf[0] == '1') {
+ i = 0;
+ while (sock_getln(server_socket, buf, sizeof buf), strcmp(buf, "000")) {
+ if (i == 1) {
+ sprintf(prompt, "\n%s> ", buf);
+ }
+ ++i;
+ }
+ }
+
+ /* Here we go ... main command loop */
+ rl_attempted_completion_function = command_name_completion;
+ while ( (cmd = readline(prompt)) , cmd ) {
+ if (*cmd) {
+ add_history(cmd);
+ ret = do_one_command(server_socket, cmd);
+ }
+ free(cmd);
+ }
+}
+
+
+/*
+ * If you don't know what main() does by now you probably shouldn't be reading this code.
+ */
+int main(int argc, char **argv)
+{
+ int server_socket = 0;
+ char buf[1024];
+ int i;
+ char *ctdldir = CTDLDIR;
+ char cmd[1024] = { 0 };
+ int exitcode = 0;
+
+ for (i = 1; i < argc; ++i) {
+ if (!strcmp(argv[i], "-h")) {
+ ctdldir = argv[++i];
+ } else {
+ if (strlen(cmd) > 0) {
+ strcat(cmd, " ");
+ }
+ strcat(cmd, argv[i]);
+ }
+ }
+
+ int is_interactive = ((strlen(cmd) == 0) ? 1 : 0);
+
+ if (is_interactive) {
+ printf("\nCitadel administration shell (c) 2009-2020 by citadel.org\n"
+ "This is open source software made available to you under the terms\n"
+ "of the GNU General Public License v3. All other rights reserved.\n");
+ printf("Connecting to Citadel server in %s...\n", ctdldir);
+ }
+
+ sprintf(buf, "%s/citadel-admin.socket", ctdldir);
+ server_socket = uds_connectsock(buf);
+ if (server_socket < 0) {
+ exit(1);
+ }
+
+ sock_getln(server_socket, buf, sizeof buf);
+ if (buf[0] == '2') {
+ if (is_interactive) {
+ printf("Connected: %s\n", buf);
+ do_main_loop(server_socket);
+ } else {
+ exitcode = do_one_command(server_socket, cmd);
+ }
+ }
+
+ sock_puts(server_socket, "QUIT");
+ sock_getln(server_socket, buf, sizeof buf);
+ if (is_interactive) {
+ printf("%s\n", buf);
+ }
+ close(server_socket);
+ exit(exitcode);
+}
/*
* Does nothing. The server should always return 200.
*/
-int CtdlIPCNoop(CtdlIPC * ipc)
-{
+int CtdlIPCNoop(CtdlIPC * ipc) {
char aaa[128];
return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
* Does nothing interesting. The server should always return 200
* along with your string.
*/
-int CtdlIPCEcho(CtdlIPC * ipc, const char *arg, char *cret)
-{
+int CtdlIPCEcho(CtdlIPC * ipc, const char *arg, char *cret) {
int ret;
char *aaa;
* Asks the server to log out. Should always return 200, even if no user
* was logged in. The user will not be logged in after this!
*/
-int CtdlIPCLogout(CtdlIPC * ipc)
-{
+int CtdlIPCLogout(CtdlIPC * ipc) {
int ret;
char aaa[SIZ];
* username is able to log in, with the username correctly spelled in cret.
* Returns various 500 error codes if the user doesn't exist, etc.
*/
-int CtdlIPCTryLogin(CtdlIPC * ipc, const char *username, char *cret)
-{
+int CtdlIPCTryLogin(CtdlIPC * ipc, const char *username, char *cret) {
int ret;
char *aaa;
* Second stage of authentication - provide password. The server returns
* 200 and several arguments in cret relating to the user's account.
*/
-int CtdlIPCTryPassword(CtdlIPC * ipc, const char *passwd, char *cret)
-{
+int CtdlIPCTryPassword(CtdlIPC * ipc, const char *passwd, char *cret) {
int ret;
char *aaa;
* user - intended for use by system administrators to create accounts on
* behalf of other users.
*/
-int CtdlIPCCreateUser(CtdlIPC * ipc, const char *username, int selfservice, char *cret)
-{
+int CtdlIPCCreateUser(CtdlIPC * ipc, const char *username, int selfservice, char *cret) {
int ret;
char *aaa;
/*
* Changes the user's password. Returns 200 if changed, errors otherwise.
*/
-int CtdlIPCChangePassword(CtdlIPC * ipc, const char *passwd, char *cret)
-{
+int CtdlIPCChangePassword(CtdlIPC * ipc, const char *passwd, char *cret) {
int ret;
char *aaa;
/* Caller must free the march list */
/* Room types are defined in enum RoomList; keep these in sync! */
/* floor is -1 for all, or floornum */
-int CtdlIPCKnownRooms(CtdlIPC * ipc, enum RoomList which, int floor, struct march **listing, char *cret)
-{
+int CtdlIPCKnownRooms(CtdlIPC * ipc, enum RoomList which, int floor, struct march **listing, char *cret) {
int ret;
struct march *march = NULL;
static char *proto[] = { "LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
/* GETU */
/* Caller must free the struct ctdluser; caller may pass an existing one */
-int CtdlIPCGetConfig(CtdlIPC * ipc, struct ctdluser **uret, char *cret)
-{
+int CtdlIPCGetConfig(CtdlIPC * ipc, struct ctdluser **uret, char *cret) {
int ret;
if (!cret)
/* SETU */
-int CtdlIPCSetConfig(CtdlIPC * ipc, struct ctdluser *uret, char *cret)
-{
+int CtdlIPCSetConfig(CtdlIPC * ipc, struct ctdluser *uret, char *cret) {
char aaa[48];
if (!uret)
/* RENU */
-int CtdlIPCRenameUser(CtdlIPC * ipc, char *oldname, char *newname, char *cret)
-{
+int CtdlIPCRenameUser(CtdlIPC * ipc, char *oldname, char *newname, char *cret) {
int ret;
char cmd[256];
/* GOTO */
-int CtdlIPCGotoRoom(CtdlIPC * ipc, const char *room, const char *passwd, struct ctdlipcroom **rret, char *cret)
-{
+int CtdlIPCGotoRoom(CtdlIPC * ipc, const char *room, const char *passwd, struct ctdlipcroom **rret, char *cret) {
int ret;
char *aaa;
--- /dev/null
+CFLAGS := -Wformat-truncation=0 -ggdb -DHAVE_OPENSSL
+LDFLAGS := -lssl -lcrypto -lz
+PREFIX := /usr/local
+BINDIR := /usr/local/bin
+CTDLDIR := /usr/local/citadel
+Method URL Function
+------ ------------------------------ -------------------------------------
+GET / Site root will redirect to a landing page
+GET /ctdl/f/ returns a JSON-encoded list of accessible floors
+GET /ctdl/r/ returns a JSON-encoded list of accessible rooms
+OPTIONS /ctdl/r/ROOMNAME/ returns just what you'd expect
+PROPFIND /ctdl/r/ROOMNAME/ Show a bunch of crap
+GET /ctdl/r/ROOMNAME/ Returns information about the room (name, view, etc.) in JSON format
+GET /ctdl/r/ROOMNAME/info.txt Returns the room info banner for this room
+GET /ctdl/r/ROOMNAME/msgs.all JSON array of message list in room
+GET /ctdl/r/ROOMNAME/msgs.new JSON array of message list in room (new messages)
+GET /ctdl/r/ROOMNAME/mailbox JSON dictionary of a mailbox summary in this room
+GET /ctdl/r/ROOMNAME/stat JSON dictionary of the server STAT command (room name and modification time)
+GET /ctdl/r/ROOMNAME/MSGNUM Retrieve the content of an individual message
+GET /ctdl/r/ROOMNAME/MSGNUM/json Retrieve an individual message in a room, encapsulated in JSON
+DELETE /ctdl/r/ROOMNAME/MSGNUM Deletes a message from a room
-Method URL Function
------- ------------------------------ -------------------------------------
-GET / Site root will redirect to a landing page
-GET /ctdl/f/ returns a JSON-encoded list of accessible floors
-GET /ctdl/r/ returns a JSON-encoded list of accessible rooms
-OPTIONS /ctdl/r/ROOMNAME/ returns just what you'd expect
-PROPFIND /ctdl/r/ROOMNAME/ Show a bunch of crap
-GET /ctdl/r/ROOMNAME/ Returns information about the room (name, view, etc.) in JSON format
-GET /ctdl/r/ROOMNAME/info.txt Returns the room info banner for this room
-GET /ctdl/r/ROOMNAME/msgs.all JSON array of message list in room
-GET /ctdl/r/ROOMNAME/msgs.new JSON array of message list in room (new messages)
-GET /ctdl/r/ROOMNAME/mailbox JSON dictionary of a mailbox summary in this room
-GET /ctdl/r/ROOMNAME/stat JSON dictionary of the server STAT command (room name and modification time)
-GET /ctdl/r/ROOMNAME/MSGNUM Retrieve the content of an individual message
-GET /ctdl/r/ROOMNAME/MSGNUM/json Retrieve an individual message in a room, encapsulated in JSON
-DELETE /ctdl/r/ROOMNAME/MSGNUM Deletes a message from a room
+PUT /ctdl/r/ROOMNAME/xxx DAV operation to insert a new message into a room
+ Accepted parameters:
+ wefw List of message references
+ subj Message subject
+ The returned ETag will be the new message number.
-PUT /ctdl/r/ROOMNAME/xxx DAV operation to insert a new message into a room
- Accepted parameters:
- wefw List of message references
- subj Message subject
- The returned ETag will be the new message number.
+GET /ctdl/r/ROOMNAME/slrp Set the "Last Read Pointer" for the room
+ Accepted parameters:
+ last The number of the most recently seen message
-GET /ctdl/r/ROOMNAME/slrp Set the "Last Read Pointer" for the room
- Accepted parameters:
- last The number of the most recently seen message
-
-GET /ctdl/c/info Returns a JSON representation of the output of an INFO server command
-POST /ctdl/a/login Send it a your credentials and it will log you in
-GET /ctdl/a/whoami
-GET /ctdl/u/USERNAME/userpic Returns an image containing the photo/avatar of the specified user
-GET /ctdl/s/ Static content (html, css, js, images...)
-GET /.well-known/ Static content (RFC5785 compliant paths)
+GET /ctdl/c/info Returns a JSON representation of the output of an INFO server command
+POST /ctdl/a/login Send it a your credentials and it will log you in
+GET /ctdl/a/whoami
+GET /ctdl/u/USERNAME/userpic Returns an image containing the photo/avatar of the specified user
+GET /ctdl/s/ Static content (html, css, js, images...)
+GET /.well-known/ Static content (RFC5785 compliant paths)
margin: 0;
}
-.ctdl-grid-item-1 { /* These grid items will be referenced in the grid container */
+.ctdl-grid-banner-item { /* These grid items will be referenced in the grid container */
grid-area: header;
}
-.ctdl-grid-item-2 {
+.ctdl-grid-navbar-item {
grid-area: menu;
}
-.ctdl-grid-item-3 {
+.ctdl-grid-main-item {
grid-area: main;
overflow-x: hidden;
overflow-y: auto;
}
-.ctdl-grid-container { /* This is so mind-bogglingly simple I can't believe it works */
+.ctdl-main-grid-container { /* This is so mind-bogglingly simple I can't believe it works */
display: grid;
grid-template-areas:
'header header header header header header'
height: 100vh;
}
-.ctdl-grid-container > div {
+.ctdl-main-grid-container > div {
background-color: #DDEEFF;
text-align: left;
padding: 10px 0;
.ctdl-sidebar-class button {
width: 100%;
- background-color: blue;
text-align: left;
background-color: #888888;
color: #FFFFFF;
}
+.ctdl-grid-banner-item button {
+ background-color: #888888;
+ color: #FFFFFF;
+}
+
.ctdl-msg-reading-pane {
background-color: #456789;
}
MODAL LOADING
</div>
-<div class="ctdl-grid-container">
+<div class="ctdl-main-grid-container">
- <div class="ctdl-grid-item-1" id="banner">
+ <div class="ctdl-grid-banner-item" id="banner">
<button onClick="sidebar_open();"><i class="fa fa-bars"></i>Menu</button>
- <span class="w3-bar-item" id="ctdl-logo">CITADEL</span>
+ <span id="ctdl-logo">CITADEL</span>
<span id="ctdl_banner_title" class="w3-bar-item">---</span>
- <button id="ctdl-newmsg-button" style="display:none" class="w3-bar-item w3-button" onClick="entmsg_dispatcher();">enter</button>
- <button id="ctdl-ungoto-button" style="display:none" class="w3-bar-item w3-button" onClick="gotonext(0);">ungoto</button>
- <button id="ctdl-skip-button" style="display:none" class="w3-bar-item w3-button" onClick="gotonext(1);">skip</button>
- <button id="ctdl-goto-button" style="display:none" class="w3-bar-item w3-button" onClick="gotonext(2);">goto</button>
- <button id="lilo" class="w3-bar-item w3-button">Login</button>
+ <button id="ctdl-newmsg-button" style="display:none" onClick="entmsg_dispatcher();">enter</button>
+ <button id="ctdl-ungoto-button" style="display:none" onClick="gotonext(0);">ungoto</button>
+ <button id="ctdl-skip-button" style="display:none" onClick="gotonext(1);">skip</button>
+ <button id="ctdl-goto-button" style="display:none" onClick="gotonext(2);">goto</button>
+ <button id="lilo">Login</button>
</div>
- <div class="ctdl-grid-item-2" id="navbar">
+ <div class="ctdl-grid-navbar-item" id="navbar">
<ul id="ctdl-sidebar" class="ctdl-sidebar-class">
<li><i class="fa fa-user-circle fa-3x"></i>
<li><span id="current_user">Not logged in.</span><br>
</ul>
</div>
- <div class="ctdl-grid-item-3" id="ctdl-main">
+ <div class="ctdl-grid-main-item" id="ctdl-main">
Loading...
</div>
-</div><!--class="ctdl-grid-container"-->
+</div><!--class="ctdl-main-grid-container"-->
<script type="text/javascript" src="js/defs.js"></script>
<script type="text/javascript" src="js/util.js"></script>