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