c2782519d3d8073da1ddd2e866eb91b61001e9cb
[citadel.git] / citadel / proxy.c
1 /*
2  * Session layer proxy for Citadel
3  * (c) 1998 by Art Cancro, All Rights Reserved, released under GNU GPL v2
4  * $Id$
5  */
6
7 /*
8  * NOTE: this isn't finished, so don't use it!!
9  *
10  */
11
12 /* Directory to put the message cache in */
13 #define CACHE_DIR       "/var/citadelproxy"
14
15 /* Number of days to keep messages in the cache */
16 #define CACHE_EXPIRE    60
17
18 /* Uncomment to enable prefetch */
19 /* #define ENABLE_PREFETCH */
20
21 /* Name and password to use for caching */
22 #define PREFETCH_USER_NAME      "cypherpunks"
23 #define PREFETCH_USER_PASSWORD  "cypherpunks"
24
25
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <sys/types.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include "citadel.h"
33
34 struct RoomList {
35         struct RoomList *next;
36         char roomname[32];
37         };
38
39 struct MsgList {
40         struct MsgList *next;
41         long msgnum;
42         };
43
44
45 /*
46  * num_parms()  -  discover number of parameters...
47  */
48 int num_parms(char *source)
49 {
50         int a;
51         int count = 1;
52
53         for (a=0; a<strlen(source); ++a)
54                 if (source[a]=='|') ++count;
55         return(count);
56         }
57
58
59 /*
60  * extract()  -  extract a parameter from a series of "|" separated...
61  */
62 void extract(char *dest, char *source, int parmnum)
63 {
64         char buf[256];
65         int count = 0;
66         int n;
67
68         n = num_parms(source);
69
70         if (parmnum >= n) {
71                 strcpy(dest,"");
72                 return;
73                 }
74         strcpy(buf,source);
75         if ( (parmnum == 0) && (n == 1) ) {
76                 strcpy(dest,buf);
77                 return;
78                 }
79
80         while (count++ < parmnum) do {
81                 strcpy(buf,&buf[1]);
82                 } while( (strlen(buf)>0) && (buf[0]!='|') );
83         if (buf[0]=='|') strcpy(buf,&buf[1]);
84         for (count = 0; count<strlen(buf); ++count)
85                 if (buf[count] == '|') buf[count] = 0;
86         strcpy(dest,buf);
87         }
88
89
90
91
92
93
94
95 void logoff(int code) {
96         exit(code);
97         }
98
99
100 /* Fetch a message (or check its status in the cache)
101  */
102 void fetch_message(long msgnum) {
103         char filename[64];
104         char temp[64];
105         FILE *fp;
106         char buf[256];
107
108         sprintf(filename, "%ld", msgnum);
109
110         if (access(filename, F_OK)==0) {
111                 return;                         /* Already on disk */
112                 }
113
114         /* The message is written to a file with a temporary name, in
115          * order to avoid another user accidentally fetching a
116          * partially written message from the cache.
117          */
118         sprintf(buf, "MSG0 %ld", msgnum);
119         serv_puts(buf);
120         serv_gets(buf);
121         if (buf[0] != '1') return;
122         sprintf(temp, "%ld.%d", msgnum, getpid());
123         fp = fopen(temp, "w");
124         while (serv_gets(buf), strcmp(buf, "000")) {
125                 fprintf(fp, "%s\n", buf);
126                 }
127         fclose(fp);
128
129         /* Now that the message is complete, it can be renamed to the
130          * filename that the cache manager will recognize it with.
131          */
132         link(temp, filename);
133         unlink(temp);
134         }
135
136
137 /*
138  * This loop pre-fetches lots of messages
139  */
140 void do_prefetch() {
141         char buf[256];
142         struct RoomList *rl = NULL;
143         struct RoomList *rlptr;
144         struct MsgList *ml = NULL;
145         struct MsgList *mlptr;
146
147         close(0);
148         close(1);
149         close(2);
150
151         serv_gets(buf);
152         if (buf[0] != '2') {
153                 exit(0);
154                 }
155
156         /* Log in (this is kind of arbitrary) */
157         sprintf(buf, "USER %s", PREFETCH_USER_NAME);
158         serv_puts(buf);
159         serv_gets(buf);
160         if (buf[0]=='3') {
161                 sprintf(buf, "PASS %s", PREFETCH_USER_PASSWORD);
162                 serv_puts(buf);
163                 serv_gets(buf);
164                 if (buf[0] != '2') exit(1);
165                 }
166         else {
167                 sprintf(buf, "NEWU %s", PREFETCH_USER_NAME);
168                 serv_puts(buf);
169                 serv_gets(buf);
170                 if (buf[0] != '2') exit(1);
171                 sprintf(buf, "SETP %s", PREFETCH_USER_PASSWORD);
172                 serv_puts(buf);
173                 serv_gets(buf);
174                 if (buf[0] != '2') exit(1);
175                 }
176
177         /* Fetch the roomlist (rooms with new messages only) */
178         serv_puts("LKRN");
179         serv_gets(buf);
180         if (buf[0] != '1') exit(2);
181         while (serv_gets(buf), strcmp(buf, "000")) {
182                 rlptr = (struct rlptr *) malloc(sizeof(struct RoomList));
183                 rlptr->next = rl;
184                 rl = rlptr;
185                 extract(rlptr->roomname, buf, 0);
186                 }
187
188         /* Go to each room, fetching new messages */
189         while (rl != NULL) {
190                 sprintf(buf, "GOTO %s", rl->roomname);
191                 serv_puts(buf);
192                 serv_gets(buf);
193                 if (buf[0]=='2') {
194                         serv_puts("MSGS NEW");
195                         serv_gets(buf);
196                         ml = NULL;
197                         if (buf[0]=='1') {
198                                 while (serv_gets(buf), strcmp(buf, "000")) {
199                                         mlptr = (struct mlptr *)
200                                                 malloc(sizeof(struct MsgList));
201                                         mlptr->next = ml;
202                                         ml = mlptr;
203                                         mlptr->msgnum = atol(buf);
204                                         }
205                                 }
206                         }
207
208                 /* Fetch each message */
209                 while (ml != NULL) {
210                         fetch_message(ml->msgnum);
211                         mlptr = ml;
212                         ml = ml->next;
213                         free(mlptr);
214                         }
215
216                 /* Free the room list pointer */
217                 rlptr = rl;
218                 rl = rl->next;
219                 free(rlptr);
220                 }
221
222         /* Now log out. */
223         serv_puts("QUIT");
224         exit(0);
225         }
226
227 void do_msg0(char cmd[]) {
228         long msgnum;
229         char filename[32];
230         char temp[32];
231         char buf[256];
232         FILE *fp;
233
234         msgnum = atol(&cmd[5]);
235         sprintf(filename, "%ld", msgnum);
236
237         /* If the message is cached, use the copy on disk */
238         fp = fopen(filename, "r");
239         if (fp != NULL) {
240                 printf("%d Cached message %ld:\n", LISTING_FOLLOWS, msgnum);
241                 while (fgets(buf, 256, fp) != NULL) {
242                         buf[strlen(buf)-1]=0;
243                         printf("%s\n", buf);
244                         }
245                 fclose(fp);
246                 printf("000\n");
247                 fflush(stdout);
248                 }
249
250         /* Otherwise, fetch the message from the server and cache it */
251         else {
252                 sprintf(buf, "MSG0 %ld", msgnum);
253                 serv_puts(buf); 
254                 serv_gets(buf);
255                 printf("%s\n", buf);
256                 fflush(stdout);
257                 if (buf[0] != '1') {
258                         return;
259                         }
260
261                 /* The message is written to a file with a temporary name, in
262                  * order to avoid another user accidentally fetching a
263                  * partially written message from the cache.
264                  */
265                 sprintf(temp, "%ld.%d", msgnum, getpid());
266                 fp = fopen(temp, "w");
267                 while (serv_gets(buf), strcmp(buf, "000")) {
268                         printf("%s\n", buf);
269                         fprintf(fp, "%s\n", buf);
270                         }
271                 printf("%s\n", buf);
272                 fflush(stdout);
273                 fclose(fp);
274
275                 /* Now that the message is complete, it can be renamed to the
276                  * filename that the cache manager will recognize it with.
277                  */
278                 link(temp, filename);
279                 unlink(temp);
280                 }
281
282         }
283
284
285 void do_mainloop() {
286         char cmd[256];
287         char resp[256];
288         char buf[4096];
289         int bytes;
290
291         while(1) {
292                 fflush(stdout);
293                 if (fgets(cmd, 256, stdin) == NULL) {
294                         serv_puts("QUIT");
295                         exit(1);
296                         }
297                 cmd[strlen(cmd)-1] = 0;
298
299                 /* QUIT commands are handled specially */
300                 if (!strncasecmp(cmd, "QUIT", 4)) {
301                         serv_puts("QUIT");
302                         printf("%d Proxy says: Bye!\n", OK);
303                         fflush(stdout);
304                         sprintf(buf,
305                           "/usr/bin/find %s -mtime +%d -exec rm -f {} \\; &",
306                           CACHE_DIR, CACHE_EXPIRE);
307                         system(buf);
308                         exit(0);
309                         }
310
311                 else if (!strncasecmp(cmd, "CHAT", 4)) {
312                         printf("%d Can't chat through the proxy ... yet.\n",
313                                 ERROR);
314                         }
315
316                 else if (!strncasecmp(cmd, "MSG0", 4)) {
317                         do_msg0(cmd);
318                         }
319
320                 /* Other commands, just pass through. */
321                 else {
322                         
323                         serv_puts(cmd);
324                         serv_gets(resp);
325                         printf("%s\n", resp);
326                         fflush(stdout);
327
328                         /* Simple command-response... */
329                         if ( (resp[0]=='2')||(resp[0]=='3')||(resp[0]=='5') ) {
330                                 }
331
332                         /* Textual input... */
333                         else if (resp[0] == '4') {
334                                 do {
335                                         if (fgets(buf, 256, stdin) == NULL) {
336                                                 exit(errno);
337                                                 }
338                                         buf[strlen(buf)-1] = 0;
339                                         serv_puts(buf);
340                                         } while (strcmp(buf, "000"));
341                                 }
342
343                         /* Textual output... */
344                         else if (resp[0] == '1') {
345                                 do {
346                                         serv_gets(buf);
347                                         printf("%s\n", buf);
348                                         } while (strcmp(buf, "000"));
349                                 }
350
351                         /* Binary output... */
352                         else if (resp[0] == '6') {
353                                 bytes = atol(&resp[4]);
354                                 serv_read(buf, bytes);
355                                 fwrite(buf, bytes, 1, stdout);
356                                 fflush(stdout);
357                                 }
358
359                         /* Binary input... */
360                         else if (resp[0] == '7') {
361                                 bytes = atol(&resp[4]);
362                                 fread(buf, bytes, 1, stdin);
363                                 serv_write(buf, bytes);
364                                 }
365
366                         /* chat... */
367                         else if (resp[0] == '8') {
368                                 sleep(2);
369                                 serv_puts("/quit");
370                                 do {
371                                         fgets(buf, 256, stdin);
372                                         buf[strlen(buf)-1] = 0;
373                                         serv_puts(buf);
374                                         } while (strcmp(buf, "000"));
375                                 }
376
377
378                         }
379                 }
380         }
381
382
383
384 void main(int argc, char *argv[]) {
385         char buf[256];
386         int pid;
387
388         /* Create the cache directory.  Die on any error *except* EEXIST
389          * because it's perfectly ok if the cache already exists.
390          */
391         if (mkdir(CACHE_DIR, 0700)!=0) {
392                 if (errno != EEXIST) {
393                         printf("%d Error creating cache directory: %s\n",
394                                 ERROR+INTERNAL_ERROR,
395                                 strerror(errno));
396                         exit(errno);
397                         }
398                 }
399
400         /* Now go there */
401         if (chdir(CACHE_DIR) != 0) exit(errno);
402
403 #ifdef ENABLE_PREFETCH
404         pid = fork();
405 #endif
406         attach_to_server(argc, argv, NULL, NULL);
407 #ifdef ENABLE_PREFETCH
408         if (pid == 0) do_prefetch();
409 #endif
410
411         serv_gets(buf);
412         strcat(buf, " (VIA PROXY)");
413         printf("%s\n", buf);
414         fflush(stdout);
415         if (buf[0] != '2') exit(0);
416
417         do_mainloop();
418         }