]> code.citadel.org Git - citadel.git/blobdiff - citadel/proxy.c
* added RCS Id keyword strings to sources
[citadel.git] / citadel / proxy.c
index e6aee06477bcf55e0de492ea61ce94d59453fc9f..13c20d2abf6ab75b56298f43c46ab0331f011755 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * 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];
@@ -40,33 +301,50 @@ void do_mainloop() {
                        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 {
+                       
                        serv_puts(cmd);
                        serv_gets(resp);
                        printf("%s\n", resp);
+                       fflush(stdout);
 
                        /* Simple command-response... */
                        if ( (resp[0]=='2')||(resp[0]=='3')||(resp[0]=='5') ) {
                                }
 
-                       /* Textual output... */
-                       else if (resp[0] == '1') {
+                       /* Textual input... */
+                       else if (resp[0] == '4') {
                                do {
-                                       serv_gets(buf);
-                                       printf("%s\n", buf);
+                                       if (fgets(buf, 256, stdin) == NULL) {
+                                               exit(errno);
+                                               }
+                                       buf[strlen(buf)-1] = 0;
+                                       serv_puts(buf);
                                        } while (strcmp(buf, "000"));
                                }
 
-                       /* Textual input... */
+                       /* Textual output... */
                        else if (resp[0] == '1') {
                                do {
-                                       fgets(buf, 256, stdin);
-                                       buf[strlen(buf)-1] = 0;
-                                       serv_puts(buf);
+                                       serv_gets(buf);
+                                       printf("%s\n", buf);
                                        } while (strcmp(buf, "000"));
                                }
 
@@ -75,6 +353,7 @@ void do_mainloop() {
                                bytes = atol(&resp[4]);
                                serv_read(buf, bytes);
                                fwrite(buf, bytes, 1, stdout);
+                               fflush(stdout);
                                }
 
                        /* Binary input... */
@@ -86,6 +365,7 @@ void do_mainloop() {
 
                        /* chat... */
                        else if (resp[0] == '8') {
+                               sleep(2);
                                serv_puts("/quit");
                                do {
                                        fgets(buf, 256, stdin);
@@ -103,8 +383,30 @@ void do_mainloop() {
 
 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)");