/*
* Session layer proxy for Citadel
* (c) 1998 by Art Cancro, All Rights Reserved, released under GNU GPL v2
+ * $Id$
*/
/*
*
*/
+/* Directory to put the message cache in */
+#define CACHE_DIR "/var/citadelproxy"
+
+/* Number of days to keep messages in the cache */
+#define CACHE_EXPIRE 60
+
+/* Uncomment to enable prefetch */
+/* #define ENABLE_PREFETCH */
+
+/* Name and password to use for caching */
+#define PREFETCH_USER_NAME "cypherpunks"
+#define PREFETCH_USER_PASSWORD "cypherpunks"
+
+
#include <stdlib.h>
#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include "citadel.h"
+struct RoomList {
+ struct RoomList *next;
+ char roomname[32];
+ };
+
+struct MsgList {
+ struct MsgList *next;
+ long msgnum;
+ };
+
+
+/*
+ * num_parms() - discover number of parameters...
+ */
+int num_parms(char *source)
+{
+ int a;
+ int count = 1;
+
+ for (a=0; a<strlen(source); ++a)
+ if (source[a]=='|') ++count;
+ return(count);
+ }
+
+
+/*
+ * extract() - extract a parameter from a series of "|" separated...
+ */
+void extract(char *dest, char *source, int parmnum)
+{
+ char buf[256];
+ int count = 0;
+ int n;
+
+ n = num_parms(source);
+
+ if (parmnum >= n) {
+ strcpy(dest,"");
+ return;
+ }
+ strcpy(buf,source);
+ if ( (parmnum == 0) && (n == 1) ) {
+ strcpy(dest,buf);
+ return;
+ }
+
+ while (count++ < parmnum) do {
+ strcpy(buf,&buf[1]);
+ } while( (strlen(buf)>0) && (buf[0]!='|') );
+ if (buf[0]=='|') strcpy(buf,&buf[1]);
+ for (count = 0; count<strlen(buf); ++count)
+ if (buf[count] == '|') buf[count] = 0;
+ strcpy(dest,buf);
+ }
+
+
+
+
+
-extern int serv_sock;
void logoff(int code) {
exit(code);
}
+/* Fetch a message (or check its status in the cache)
+ */
+void fetch_message(long msgnum) {
+ char filename[64];
+ char temp[64];
+ FILE *fp;
+ char buf[256];
+
+ sprintf(filename, "%ld", msgnum);
+
+ if (access(filename, F_OK)==0) {
+ return; /* Already on disk */
+ }
+
+ /* The message is written to a file with a temporary name, in
+ * order to avoid another user accidentally fetching a
+ * partially written message from the cache.
+ */
+ sprintf(buf, "MSG0 %ld", msgnum);
+ serv_puts(buf);
+ serv_gets(buf);
+ if (buf[0] != '1') return;
+ sprintf(temp, "%ld.%d", msgnum, getpid());
+ fp = fopen(temp, "w");
+ while (serv_gets(buf), strcmp(buf, "000")) {
+ fprintf(fp, "%s\n", buf);
+ }
+ fclose(fp);
+
+ /* Now that the message is complete, it can be renamed to the
+ * filename that the cache manager will recognize it with.
+ */
+ link(temp, filename);
+ unlink(temp);
+ }
+
+
+/*
+ * This loop pre-fetches lots of messages
+ */
+void do_prefetch() {
+ char buf[256];
+ struct RoomList *rl = NULL;
+ struct RoomList *rlptr;
+ struct MsgList *ml = NULL;
+ struct MsgList *mlptr;
+
+ close(0);
+ close(1);
+ close(2);
+
+ serv_gets(buf);
+ if (buf[0] != '2') {
+ exit(0);
+ }
+
+ /* Log in (this is kind of arbitrary) */
+ sprintf(buf, "USER %s", PREFETCH_USER_NAME);
+ serv_puts(buf);
+ serv_gets(buf);
+ if (buf[0]=='3') {
+ sprintf(buf, "PASS %s", PREFETCH_USER_PASSWORD);
+ serv_puts(buf);
+ serv_gets(buf);
+ if (buf[0] != '2') exit(1);
+ }
+ else {
+ sprintf(buf, "NEWU %s", PREFETCH_USER_NAME);
+ serv_puts(buf);
+ serv_gets(buf);
+ if (buf[0] != '2') exit(1);
+ sprintf(buf, "SETP %s", PREFETCH_USER_PASSWORD);
+ serv_puts(buf);
+ serv_gets(buf);
+ if (buf[0] != '2') exit(1);
+ }
+
+ /* Fetch the roomlist (rooms with new messages only) */
+ serv_puts("LKRN");
+ serv_gets(buf);
+ if (buf[0] != '1') exit(2);
+ while (serv_gets(buf), strcmp(buf, "000")) {
+ rlptr = (struct rlptr *) malloc(sizeof(struct RoomList));
+ rlptr->next = rl;
+ rl = rlptr;
+ extract(rlptr->roomname, buf, 0);
+ }
+
+ /* Go to each room, fetching new messages */
+ while (rl != NULL) {
+ sprintf(buf, "GOTO %s", rl->roomname);
+ serv_puts(buf);
+ serv_gets(buf);
+ if (buf[0]=='2') {
+ serv_puts("MSGS NEW");
+ serv_gets(buf);
+ ml = NULL;
+ if (buf[0]=='1') {
+ while (serv_gets(buf), strcmp(buf, "000")) {
+ mlptr = (struct mlptr *)
+ malloc(sizeof(struct MsgList));
+ mlptr->next = ml;
+ ml = mlptr;
+ mlptr->msgnum = atol(buf);
+ }
+ }
+ }
+
+ /* Fetch each message */
+ while (ml != NULL) {
+ fetch_message(ml->msgnum);
+ mlptr = ml;
+ ml = ml->next;
+ free(mlptr);
+ }
+
+ /* Free the room list pointer */
+ rlptr = rl;
+ rl = rl->next;
+ free(rlptr);
+ }
+
+ /* Now log out. */
+ serv_puts("QUIT");
+ exit(0);
+ }
+
+void do_msg0(char cmd[]) {
+ long msgnum;
+ char filename[32];
+ char temp[32];
+ char buf[256];
+ FILE *fp;
+
+ msgnum = atol(&cmd[5]);
+ sprintf(filename, "%ld", msgnum);
+
+ /* If the message is cached, use the copy on disk */
+ fp = fopen(filename, "r");
+ if (fp != NULL) {
+ printf("%d Cached message %ld:\n", LISTING_FOLLOWS, msgnum);
+ while (fgets(buf, 256, fp) != NULL) {
+ buf[strlen(buf)-1]=0;
+ printf("%s\n", buf);
+ }
+ fclose(fp);
+ printf("000\n");
+ fflush(stdout);
+ }
+
+ /* Otherwise, fetch the message from the server and cache it */
+ else {
+ sprintf(buf, "MSG0 %ld", msgnum);
+ serv_puts(buf);
+ serv_gets(buf);
+ printf("%s\n", buf);
+ fflush(stdout);
+ if (buf[0] != '1') {
+ return;
+ }
+
+ /* The message is written to a file with a temporary name, in
+ * order to avoid another user accidentally fetching a
+ * partially written message from the cache.
+ */
+ sprintf(temp, "%ld.%d", msgnum, getpid());
+ fp = fopen(temp, "w");
+ while (serv_gets(buf), strcmp(buf, "000")) {
+ printf("%s\n", buf);
+ fprintf(fp, "%s\n", buf);
+ }
+ printf("%s\n", buf);
+ fflush(stdout);
+ fclose(fp);
+
+ /* Now that the message is complete, it can be renamed to the
+ * filename that the cache manager will recognize it with.
+ */
+ link(temp, filename);
+ unlink(temp);
+ }
+
+ }
+
+
void do_mainloop() {
char cmd[256];
char resp[256];
serv_puts("QUIT");
printf("%d Proxy says: Bye!\n", OK);
fflush(stdout);
+ sprintf(buf,
+ "/usr/bin/find %s -mtime +%d -exec rm -f {} \\; &",
+ CACHE_DIR, CACHE_EXPIRE);
+ system(buf);
exit(0);
}
+ else if (!strncasecmp(cmd, "CHAT", 4)) {
+ printf("%d Can't chat through the proxy ... yet.\n",
+ ERROR);
+ }
+
+ else if (!strncasecmp(cmd, "MSG0", 4)) {
+ do_msg0(cmd);
+ }
+
/* Other commands, just pass through. */
else {
void main(int argc, char *argv[]) {
char buf[256];
+ int pid;
+
+ /* Create the cache directory. Die on any error *except* EEXIST
+ * because it's perfectly ok if the cache already exists.
+ */
+ if (mkdir(CACHE_DIR, 0700)!=0) {
+ if (errno != EEXIST) {
+ printf("%d Error creating cache directory: %s\n",
+ ERROR+INTERNAL_ERROR,
+ strerror(errno));
+ exit(errno);
+ }
+ }
+
+ /* Now go there */
+ if (chdir(CACHE_DIR) != 0) exit(errno);
-
+#ifdef ENABLE_PREFETCH
+ pid = fork();
+#endif
attach_to_server(argc, argv);
+#ifdef ENABLE_PREFETCH
+ if (pid == 0) do_prefetch();
+#endif
serv_gets(buf);
strcat(buf, " (VIA PROXY)");