* Added in some infrastructure to switch the client's message reading from
[citadel.git] / citadel / citadel_ipc.c
1 /* $Id$ */
2
3 #include "sysdep.h"
4 #if TIME_WITH_SYS_TIME
5 # include <sys/time.h>
6 # include <time.h>
7 #else
8 # if HAVE_SYS_TIME_H
9 #  include <sys/time.h>
10 # else
11 #  include <time.h>
12 # endif
13 #endif
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <string.h>
17 #include <malloc.h>
18 #include <stdlib.h>
19 #include <ctype.h>
20 #ifdef THREADED_CLIENT
21 #include <pthread.h>
22 #endif
23 #include "citadel.h"
24 #include "citadel_ipc.h"
25 #include "client_crypto.h"
26 #include "tools.h"
27
28 #ifdef THREADED_CLIENT
29 pthread_mutex_t rwlock;
30 #endif
31 char express_msgs = 0;
32
33 static volatile int download_in_progress = 0;   /* download file open */
34 static volatile int upload_in_progress = 0;     /* upload file open */
35 /* static volatile int serv_sock; */    /* Socket on which we talk to server */
36
37
38 /*
39  * Does nothing.  The server should always return 200.
40  */
41 int CtdlIPCNoop(void)
42 {
43         char aaa[128];
44
45         return CtdlIPCGenericCommand("NOOP", NULL, 0, NULL, NULL, aaa);
46 }
47
48
49 /*
50  * Does nothing interesting.  The server should always return 200
51  * along with your string.
52  */
53 int CtdlIPCEcho(const char *arg, char *cret)
54 {
55         register int ret;
56         char *aaa;
57         
58         if (!arg) return -2;
59         if (!cret) return -2;
60
61         aaa = (char *)malloc(strlen(arg) + 6);
62         if (!aaa) return -1;
63
64         sprintf(aaa, "ECHO %s", arg);
65         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
66         free(aaa);
67         return ret;
68 }
69
70
71 /*
72  * Asks the server to close the connecction.
73  * Should always return 200.
74  */
75 int CtdlIPCQuit(void)
76 {
77         register int ret;
78         char aaa[128];
79
80         netio_lock();
81         serv_puts("QUIT");
82         serv_gets(aaa);
83         ret = atoi(aaa);
84         netio_unlock();
85         return ret;
86 }
87
88
89 /*
90  * Asks the server to logout.  Should always return 200, even if no user
91  * was logged in.  The user will not be logged in after this!
92  */
93 int CtdlIPCLogout(void)
94 {
95         register int ret;
96         char aaa[128];
97
98         netio_lock();
99         serv_puts("LOUT");
100         serv_gets(aaa);
101         ret = atoi(aaa);
102         netio_unlock();
103         return ret;
104 }
105
106
107 /*
108  * First stage of authentication - pass the username.  Returns 300 if the
109  * username is able to log in, with the username correctly spelled in cret.
110  * Returns various 500 error codes if the user doesn't exist, etc.
111  */
112 int CtdlIPCTryLogin(const char *username, char *cret)
113 {
114         register int ret;
115         char *aaa;
116
117         if (!username) return -2;
118         if (!cret) return -2;
119
120         aaa = (char *)malloc(strlen(username) + 6);
121         if (!aaa) return -1;
122
123         sprintf(aaa, "USER %s", username);
124         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
125         free(aaa);
126         return ret;
127 }
128
129
130 /*
131  * Second stage of authentication - provide password.  The server returns
132  * 200 and several arguments in cret relating to the user's account.
133  */
134 int CtdlIPCTryPassword(const char *passwd, char *cret)
135 {
136         register int ret;
137         char *aaa;
138
139         if (!passwd) return -2;
140         if (!cret) return -2;
141
142         aaa = (char *)malloc(strlen(passwd) + 6);
143         if (!aaa) return -1;
144
145         sprintf(aaa, "PASS %s", passwd);
146         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
147         free(aaa);
148         return ret;
149 }
150
151
152 /*
153  * Create a new user.  This returns 200 plus the same arguments as TryPassword
154  * if selfservice is nonzero, unless there was a problem creating the account.
155  * If selfservice is zero, creates a new user but does not log out the existing
156  * user - intended for use by system administrators to create accounts on
157  * behalf of other users.
158  */
159 int CtdlIPCCreateUser(const char *username, int selfservice, char *cret)
160 {
161         register int ret;
162         char *aaa;
163
164         if (!username) return -2;
165         if (!cret) return -2;
166
167         aaa = (char *)malloc(strlen(username) + 6);
168         if (!aaa) return -1;
169
170         sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
171         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
172         free(aaa);
173         return ret;
174 }
175
176
177 /*
178  * Changes the user's password.  Returns 200 if changed, errors otherwise.
179  */
180 int CtdlIPCChangePassword(const char *passwd, char *cret)
181 {
182         register int ret;
183         char *aaa;
184
185         if (!passwd) return -2;
186         if (!cret) return -2;
187
188         aaa = (char *)malloc(strlen(passwd) + 6);
189         if (!aaa) return -1;
190
191         sprintf(aaa, "SETP %s", passwd);
192         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
193         free(aaa);
194         return ret;
195 }
196
197
198 /* LKRN */
199 /* Caller must free the march list */
200 /* which is 0 = LRMS, 1 = LKRN, 2 = LKRO, 3 = LKRA, 4 = LZRM */
201 /* floor is -1 for all, or floornum */
202 int CtdlIPCKnownRooms(int which, int floor, struct march **listing, char *cret)
203 {
204         register int ret;
205         struct march *march = NULL;
206         static char *proto[] = {"LRMS", "LKRN", "LKRO", "LKRA", "LZRM" };
207         char aaa[SIZ];
208         char *bbb = NULL;
209         size_t bbbsize;
210
211         if (!listing) return -2;
212         if (*listing) return -2;        /* Free the listing first */
213         if (!cret) return -2;
214         if (which < 0 || which > 4) return -2;
215         if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
216
217         sprintf(aaa, "%s %d", proto[which], floor);
218         ret = CtdlIPCGenericCommand(aaa, NULL, 0, &bbb, &bbbsize, cret);
219         if (ret / 100 == 1) {
220                 struct march *mptr;
221
222                 while (bbb && strlen(bbb)) {
223                         int a;
224
225                         extract_token(aaa, bbb, 0, '\n');
226                         a = strlen(aaa);
227                         memmove(bbb, bbb + a + 1, strlen(bbb) - a);
228                         mptr = (struct march *) malloc(sizeof (struct march));
229                         if (mptr) {
230                                 mptr->next = NULL;
231                                 extract(mptr->march_name, aaa, 0);
232                                 mptr->march_floor = (char) extract_int(aaa, 2);
233                                 mptr->march_order = (char) extract_int(aaa, 3);
234                                 if (march == NULL)
235                                         march = mptr;
236                                 else {
237                                         struct march *mptr2;
238
239                                         mptr2 = march;
240                                         while (mptr2->next != NULL)
241                                                 mptr2 = mptr2->next;
242                                         mptr2->next = mptr;
243                                 }
244                         }
245                 }
246         }
247         *listing = march;
248         return ret;
249 }
250
251
252 /* GETU */
253 /* Caller must free the struct usersupp; caller may pass an existing one */
254 int CtdlIPCGetConfig(struct usersupp **uret, char *cret)
255 {
256         register int ret;
257
258         if (!cret) return -2;
259         if (!uret) return -2;
260         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof (struct usersupp));
261         if (!*uret) return -1;
262
263         ret = CtdlIPCGenericCommand("GETU", NULL, 0, NULL, NULL, cret);
264         if (ret / 100 == 2) {
265                 uret[0]->USscreenwidth = extract_int(cret, 0);
266                 uret[0]->USscreenheight = extract_int(cret, 1);
267                 uret[0]->flags = extract_int(cret, 2);
268         }
269         return ret;
270 }
271
272
273 /* SETU */
274 int CtdlIPCSetConfig(struct usersupp *uret, char *cret)
275 {
276         char aaa[48];
277
278         if (!uret) return -2;
279         if (!cret) return -2;
280
281         sprintf(aaa, "SETU %d|%d|%d",
282                         uret->USscreenwidth, uret->USscreenheight,
283                         uret->flags);
284         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
285 }
286
287
288 /* GOTO */
289 int CtdlIPCGotoRoom(const char *room, const char *passwd,
290                 struct ctdlipcroom **rret, char *cret)
291 {
292         register int ret;
293         char *aaa;
294
295         if (!cret) return -2;
296         if (!rret) return -2;
297         if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
298         if (!*rret) return -1;
299
300         if (passwd) {
301                 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
302                 if (!aaa) {
303                         free(*rret);
304                         return -1;
305                 }
306                 sprintf(aaa, "GOTO %s|%s", room, passwd);
307         } else {
308                 aaa = (char *)malloc(strlen(room) + 6);
309                 if (!aaa) {
310                         free(*rret);
311                         return -1;
312                 }
313                 sprintf(aaa, "GOTO %s", room);
314         }
315         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
316         if (ret / 100 == 2) {
317                 extract(rret[0]->RRname, cret, 0);
318                 rret[0]->RRunread = extract_long(cret, 1);
319                 rret[0]->RRtotal = extract_long(cret, 2);
320                 rret[0]->RRinfoupdated = extract_int(cret, 3);
321                 rret[0]->RRflags = extract_int(cret, 4);
322                 rret[0]->RRhighest = extract_long(cret, 5);
323                 rret[0]->RRlastread = extract_long(cret, 6);
324                 rret[0]->RRismailbox = extract_int(cret, 7);
325                 rret[0]->RRaide = extract_int(cret, 8);
326                 rret[0]->RRnewmail = extract_long(cret, 9);
327                 rret[0]->RRfloor = extract_int(cret, 10);
328         } else {
329                 free(*rret);
330         }
331         return ret;
332 }
333
334
335 /* MSGS */
336 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
337 /* whicharg is number of messages, applies to last, first, gt, lt */
338 int CtdlIPCGetMessages(int which, int whicharg, const char *template,
339                 long **mret, char *cret)
340 {
341         register int ret;
342         register long count = 0;
343         static char *proto[] =
344                 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
345         char aaa[33];
346         char *bbb;
347         size_t bbbsize;
348
349         if (!cret) return -2;
350         if (!mret) return -2;
351         if (*mret) return -2;
352         if (which < 0 || which > 6) return -2;
353
354         if (which <= 2)
355                 sprintf(aaa, "MSGS %s||%d", proto[which],
356                                 (template) ? 1 : 0);
357         else
358                 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
359                                 (template) ? 1 : 0);
360         if (template) count = strlen(template);
361         ret = CtdlIPCGenericCommand(aaa, template, count, &bbb, &bbbsize, cret);
362         count = 0;
363         while (strlen(bbb)) {
364                 int a;
365
366                 extract_token(aaa, bbb, 0, '\n');
367                 a = strlen(aaa);
368                 memmove(aaa, bbb + a + 1, strlen(bbb) - a - 1);
369                 *mret = (long *)realloc(mret, (count + 1) * sizeof (long));
370                 if (*mret)
371                         *mret[count++] = atol(aaa);
372                 *mret[count] = 0L;
373         }
374         return ret;
375 }
376
377
378 /* MSG0, MSG2 */
379 int CtdlIPCGetSingleMessage(long msgnum, int headers, int as_mime,
380                 struct ctdlipcmessage **mret, char *cret)
381 {
382         register int ret;
383         char aaa[SIZ];
384         char *bbb = NULL;
385         size_t bbbsize;
386         int multipart_hunting = 0;
387         char multipart_prefix[SIZ];
388
389         if (!cret) return -1;
390         if (!mret) return -1;
391         if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
392         if (!*mret) return -1;
393         if (!msgnum) return -1;
394
395         sprintf(aaa, "MSG%c %ld|%d", as_mime ? '2' : '0', msgnum, headers);
396         ret = CtdlIPCGenericCommand(aaa, NULL, 0, &bbb, &bbbsize, cret);
397         if (ret / 100 == 1) {
398                 if (!as_mime) {
399                         strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
400                         while (strlen(bbb) > 4 && bbb[4] == '=') {
401                                 extract_token(aaa, bbb, 0, '\n');
402                                 remove_token(bbb, 0, '\n');
403
404                                 if (!strncasecmp(aaa, "nhdr=yes", 8))
405                                         mret[0]->nhdr = 1;
406                                 else if (!strncasecmp(aaa, "from=", 5))
407                                         strcpy(mret[0]->author, &aaa[5]);
408                                 else if (!strncasecmp(aaa, "type=", 5))
409                                         mret[0]->type = atoi(&aaa[5]);
410                                 else if (!strncasecmp(aaa, "msgn=", 5))
411                                         strcpy(mret[0]->msgid, &aaa[5]);
412                                 else if (!strncasecmp(aaa, "subj=", 5))
413                                         strcpy(mret[0]->subject, &aaa[5]);
414                                 else if (!strncasecmp(aaa, "rfca=", 5))
415                                         strcpy(mret[0]->email, &aaa[5]);
416                                 else if (!strncasecmp(aaa, "hnod=", 5))
417                                         strcpy(mret[0]->hnod, &aaa[5]);
418                                 else if (!strncasecmp(aaa, "room=", 5))
419                                         strcpy(mret[0]->room, &aaa[5]);
420                                 else if (!strncasecmp(aaa, "node=", 5))
421                                         strcpy(mret[0]->node, &aaa[5]);
422                                 else if (!strncasecmp(aaa, "rcpt=", 5))
423                                         strcpy(mret[0]->recipient, &aaa[5]);
424                                 else if (!strncasecmp(aaa, "time=", 5))
425                                         mret[0]->time = atol(&aaa[5]);
426
427                                 /* Multipart/alternative prefix & suffix strings help
428                                  * us to determine which part we want to download.
429                                  */
430                                 else if (!strncasecmp(aaa, "pref=", 5)) {
431                                         extract(multipart_prefix, &aaa[5], 1);
432                                         if (!strcasecmp(multipart_prefix,
433                                            "multipart/alternative")) {
434                                                 ++multipart_hunting;
435                                         }
436                                 }
437                                 else if (!strncasecmp(aaa, "suff=", 5)) {
438                                         extract(multipart_prefix, &aaa[5], 1);
439                                         if (!strcasecmp(multipart_prefix,
440                                            "multipart/alternative")) {
441                                                 ++multipart_hunting;
442                                         }
443                                 }
444
445                                 else if (!strncasecmp(aaa, "part=", 5)) {
446                                         struct parts *ptr, *chain;
447         
448                                         ptr = (struct parts *)calloc(1, sizeof (struct parts));
449                                         if (ptr) {
450
451                                                 /* Fill the buffers for the caller */
452                                                 extract(ptr->name, &aaa[5], 0);
453                                                 extract(ptr->filename, &aaa[5], 1);
454                                                 extract(ptr->number, &aaa[5], 2);
455                                                 extract(ptr->disposition, &aaa[5], 3);
456                                                 extract(ptr->mimetype, &aaa[5], 4);
457                                                 ptr->length = extract_long(&aaa[5], 5);
458                                                 if (!mret[0]->attachments)
459                                                         mret[0]->attachments = ptr;
460                                                 else {
461                                                         chain = mret[0]->attachments;
462                                                         while (chain->next)
463                                                                 chain = chain->next;
464                                                         chain->next = ptr;
465                                                 }
466
467                                                 /* Now handle multipart/alternative */
468                                                 if (multipart_hunting > 0) {
469                                                         if ( (!strcasecmp(ptr->mimetype,
470                                                              "text/plain"))
471                                                            || (!strcasecmp(ptr->mimetype,
472                                                               "text/html")) ) {
473                                                                 strcpy(mret[0]->mime_chosen,
474                                                                         ptr->number);
475                                                         }
476                                                 }
477
478                                         }
479                                 }
480                         }
481                         /* Eliminate "text\n" */
482                         remove_token(bbb, 0, '\n');
483                 }
484                 if (strlen(bbb)) {
485                         /* Strip trailing whitespace */
486                         bbb = (char *)realloc(bbb, strlen(bbb) + 1);
487                 } else {
488                         bbb = (char *)realloc(bbb, 1);
489                         *bbb = '\0';
490                 }
491                 mret[0]->text = bbb;
492         }
493         return ret;
494 }
495
496
497 /* WHOK */
498 int CtdlIPCWhoKnowsRoom(char **listing, char *cret)
499 {
500         register int ret;
501         size_t bytes;
502
503         if (!cret) return -2;
504         if (!listing) return -2;
505         if (*listing) return -2;
506
507         ret = CtdlIPCGenericCommand("WHOK", NULL, 0, listing, &bytes, cret);
508         return ret;
509 }
510
511
512 /* INFO */
513 int CtdlIPCServerInfo(struct CtdlServInfo *ServInfo, char *cret)
514 {
515         register int ret;
516         size_t bytes;
517         char *listing = NULL;
518         char buf[SIZ];
519
520         if (!cret) return -2;
521         if (!ServInfo) return -2;
522
523         ret = CtdlIPCGenericCommand("INFO", NULL, 0, &listing, &bytes, cret);
524         if (ret / 100 == 1) {
525                 int line = 0;
526
527                 while (*listing && strlen(listing)) {
528                         extract_token(buf, listing, 0, '\n');
529                         remove_token(listing, 0, '\n');
530                         switch (line++) {
531                         case 0:         ServInfo->serv_pid = atoi(buf);
532                                         break;
533                         case 1:         strcpy(ServInfo->serv_nodename,buf);
534                                         break;
535                         case 2:         strcpy(ServInfo->serv_humannode,buf);
536                                         break;
537                         case 3:         strcpy(ServInfo->serv_fqdn,buf);
538                                         break;
539                         case 4:         strcpy(ServInfo->serv_software,buf);
540                                         break;
541                         case 5:         ServInfo->serv_rev_level = atoi(buf);
542                                         break;
543                         case 6:         strcpy(ServInfo->serv_bbs_city,buf);
544                                         break;
545                         case 7:         strcpy(ServInfo->serv_sysadm,buf);
546                                         break;
547                         case 9:         strcpy(ServInfo->serv_moreprompt,buf);
548                                         break;
549                         case 10:        ServInfo->serv_ok_floors = atoi(buf);
550                                         break;
551                         case 11:        ServInfo->serv_paging_level = atoi(buf);
552                                         break;
553                         case 13:        ServInfo->serv_supports_qnop = atoi(buf);
554                                         break;
555                         }
556                 }
557
558         }
559         return ret;
560 }
561
562
563 /* RDIR */
564 int CtdlIPCReadDirectory(char **listing, char *cret)
565 {
566         register int ret;
567         size_t bytes;
568
569         if (!cret) return -2;
570         if (!listing) return -2;
571         if (*listing) return -2;
572
573         ret = CtdlIPCGenericCommand("RDIR", NULL, 0, listing, &bytes, cret);
574         return ret;
575 }
576
577
578 /*
579  * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
580  */
581 int CtdlIPCSetLastRead(long msgnum, char *cret)
582 {
583         register int ret;
584         char aaa[16];
585
586         if (!cret) return -2;
587
588         if (msgnum)
589                 sprintf(aaa, "SLRP %ld", msgnum);
590         else
591                 sprintf(aaa, "SLRP HIGHEST");
592         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
593         return ret;
594 }
595
596
597 /* INVT */
598 int CtdlIPCInviteUserToRoom(const char *username, char *cret)
599 {
600         register int ret;
601         char *aaa;
602
603         if (!cret) return -2;
604         if (!username) return -2;
605
606         aaa = (char *)malloc(strlen(username) + 6);
607         if (!aaa) return -1;
608
609         sprintf(aaa, "INVT %s", username);
610         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
611         free(aaa);
612         return ret;
613 }
614
615
616 /* KICK */
617 int CtdlIPCKickoutUserFromRoom(const char *username, char *cret)
618 {
619         register int ret;
620         char *aaa;
621
622         if (!cret) return -1;
623         if (!username) return -1;
624
625         aaa = (char *)malloc(strlen(username) + 6);
626
627         sprintf(aaa, "KICK %s", username);
628         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
629         free(aaa);
630         return ret;
631 }
632
633
634 /* GETR */
635 int CtdlIPCGetRoomAttributes(struct quickroom **qret, char *cret)
636 {
637         register int ret;
638
639         if (!cret) return -2;
640         if (!qret) return -2;
641         if (!*qret) *qret = (struct quickroom *)calloc(1, sizeof (struct quickroom));
642         if (!*qret) return -1;
643
644         ret = CtdlIPCGenericCommand("GETR", NULL, 0, NULL, NULL, cret);
645         if (ret / 100 == 2) {
646                 extract(qret[0]->QRname, cret, 0);
647                 extract(qret[0]->QRpasswd, cret, 1);
648                 extract(qret[0]->QRdirname, cret, 2);
649                 qret[0]->QRflags = extract_int(cret, 3);
650                 qret[0]->QRfloor = extract_int(cret, 4);
651                 qret[0]->QRorder = extract_int(cret, 5);
652         }
653         return ret;
654 }
655
656
657 /* SETR */
658 /* set forget to kick all users out of room */
659 int CtdlIPCSetRoomAttributes(int forget, struct quickroom *qret, char *cret)
660 {
661         register int ret;
662         char *aaa;
663
664         if (!cret) return -2;
665         if (!qret) return -2;
666
667         aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
668                         strlen(qret->QRdirname) + 52);
669         if (!aaa) return -1;
670
671         sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d",
672                         qret->QRname, qret->QRpasswd, qret->QRdirname,
673                         qret->QRflags, forget, qret->QRfloor, qret->QRorder);
674         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
675         free(aaa);
676         return ret;
677 }
678
679
680 /* GETA */
681 int CtdlIPCGetRoomAide(char *cret)
682 {
683         if (!cret) return -1;
684
685         return CtdlIPCGenericCommand("GETA", NULL, 0, NULL, NULL, cret);
686 }
687
688
689 /* SETA */
690 int CtdlIPCSetRoomAide(const char *username, char *cret)
691 {
692         register int ret;
693         char *aaa;
694
695         if (!cret) return -2;
696         if (!username) return -2;
697
698         aaa = (char *)malloc(strlen(username) + 6);
699         if (!aaa) return -1;
700
701         sprintf(aaa, "SETA %s", username);
702         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
703         free(aaa);
704         return ret;
705 }
706
707
708 /* ENT0 */
709 int CtdlIPCPostMessage(int flag, const struct ctdlipcmessage *mr, char *cret)
710 {
711         register int ret;
712         char *aaa;
713
714         if (!cret) return -2;
715         if (!mr) return -2;
716
717         aaa = (char *)malloc(strlen(mr->recipient) + strlen(mr->author) + 40);
718         if (!aaa) return -1;
719
720         sprintf(aaa, "ENT0 %d|%s|%d|%d|%s", flag, mr->recipient, mr->anonymous,
721                         mr->type, mr->author);
722         ret = CtdlIPCGenericCommand(aaa, mr->text, strlen(mr->text), NULL,
723                         NULL, cret);
724         free(aaa);
725         return ret;
726 }
727
728
729 /* RINF */
730 int CtdlIPCRoomInfo(char **iret, char *cret)
731 {
732         size_t bytes;
733
734         if (!cret) return -2;
735         if (!iret) return -2;
736         if (*iret) return -2;
737
738         return CtdlIPCGenericCommand("RINF", NULL, 0, iret, &bytes, cret);
739 }
740
741
742 /* DELE */
743 int CtdlIPCDeleteMessage(long msgnum, char *cret)
744 {
745         char aaa[16];
746
747         if (!cret) return -2;
748         if (!msgnum) return -2;
749
750         sprintf(aaa, "DELE %ld", msgnum);
751         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
752 }
753
754
755 /* MOVE */
756 int CtdlIPCMoveMessage(int copy, long msgnum, const char *destroom, char *cret)
757 {
758         register int ret;
759         char *aaa;
760
761         if (!cret) return -2;
762         if (!destroom) return -2;
763         if (!msgnum) return -2;
764
765         aaa = (char *)malloc(strlen(destroom) + 28);
766         if (!aaa) return -1;
767
768         sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
769         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
770         free(aaa);
771         return ret;
772 }
773
774
775 /* KILL */
776 int CtdlIPCDeleteRoom(int for_real, char *cret)
777 {
778         char aaa[16];
779
780         if (!cret) return -2;
781
782         sprintf(aaa, "KILL %d", for_real);
783         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
784 }
785
786
787 /* CRE8 */
788 int CtdlIPCCreateRoom(int for_real, const char *roomname, int type,
789                 const char *password, int floor, char *cret)
790 {
791         register int ret;
792         char *aaa;
793
794         if (!cret) return -2;
795         if (!roomname) return -2;
796
797         if (password) {
798                 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
799                 if (!aaa) return -1;
800                 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
801                                 password, floor);
802         } else {
803                 aaa = (char *)malloc(strlen(roomname) + 40);
804                 if (!aaa) return -1;
805                 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
806                                 floor);
807         }
808         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
809         free(aaa);
810         return ret;
811 }
812
813
814 /* FORG */
815 int CtdlIPCForgetRoom(char *cret)
816 {
817         if (!cret) return -2;
818
819         return CtdlIPCGenericCommand("FORG", NULL, 0, NULL, NULL, cret);
820 }
821
822
823 /* MESG */
824 int CtdlIPCSystemMessage(const char *message, char **mret, char *cret)
825 {
826         register int ret;
827         char *aaa;
828         size_t bytes;
829
830         if (!cret) return -2;
831         if (!mret) return -2;
832         if (*mret) return -2;
833         if (!message) return -2;
834
835         aaa = (char *)malloc(strlen(message) + 6);
836         if (!aaa) return -1;
837
838         sprintf(aaa, "MESG %s", message);
839         ret = CtdlIPCGenericCommand(aaa, NULL, 0, mret, &bytes, cret);
840         free(aaa);
841         return ret;
842 }
843
844
845 /* GNUR */
846 int CtdlIPCNextUnvalidatedUser(char *cret)
847 {
848         if (!cret) return -2;
849
850         return CtdlIPCGenericCommand("GNUR", NULL, 0, NULL, NULL, cret);
851 }
852
853
854 /* GREG */
855 int CtdlIPCGetUserRegistration(const char *username, char **rret, char *cret)
856 {
857         register int ret;
858         char *aaa;
859         size_t bytes;
860
861         if (!cret) return -2;
862         if (!rret) return -2;
863         if (*rret) return -2;
864
865         if (username)
866                 aaa = (char *)malloc(strlen(username) + 6);
867         else
868                 aaa = (char *)malloc(12);
869         if (!aaa) return -1;
870
871         if (username)
872                 sprintf(aaa, "GREG %s", username);
873         else
874                 sprintf(aaa, "GREG _SELF_");
875         ret = CtdlIPCGenericCommand(aaa, NULL, 0, rret, &bytes, cret);
876         free(aaa);
877         return ret;
878 }
879
880
881 /* VALI */
882 int CtdlIPCValidateUser(const char *username, int axlevel, char *cret)
883 {
884         register int ret;
885         char *aaa;
886
887         if (!cret) return -2;
888         if (!username) return -2;
889         if (axlevel < 0 || axlevel > 7) return -2;
890
891         aaa = (char *)malloc(strlen(username) + 17);
892         if (!aaa) return -1;
893
894         sprintf(aaa, "VALI %s|%d", username, axlevel);
895         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
896         free(aaa);
897         return ret;
898 }
899
900
901 /* EINF */
902 int CtdlIPCSetRoomInfo(int for_real, const char *info, char *cret)
903 {
904         char aaa[16];
905
906         if (!cret) return -1;
907         if (!info) return -1;
908
909         sprintf(aaa, "EINF %d", for_real);
910         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
911 }
912
913
914 /* LIST */
915 int CtdlIPCUserListing(char **listing, char *cret)
916 {
917         size_t bytes;
918
919         if (!cret) return -1;
920         if (!listing) return -1;
921         if (*listing) return -1;
922
923         return CtdlIPCGenericCommand("LIST", NULL, 0, listing, &bytes, cret);
924 }
925
926
927 /* REGI */
928 int CtdlIPCSetRegistration(const char *info, char *cret)
929 {
930         if (!cret) return -1;
931         if (!info) return -1;
932
933         return CtdlIPCGenericCommand("REGI", info, strlen(info),
934                         NULL, NULL, cret);
935 }
936
937
938 /* CHEK */
939 int CtdlIPCMiscCheck(struct ctdlipcmisc *chek, char *cret)
940 {
941         register int ret;
942
943         if (!cret) return -1;
944         if (!chek) return -1;
945
946         ret = CtdlIPCGenericCommand("CHEK", NULL, 0, NULL, NULL, cret);
947         if (ret / 100 == 2) {
948                 chek->newmail = extract_long(cret, 0);
949                 chek->needregis = extract_int(cret, 1);
950                 chek->needvalid = extract_int(cret, 2);
951         }
952         return ret;
953 }
954
955
956 /* DELF */
957 int CtdlIPCDeleteFile(const char *filename, char *cret)
958 {
959         register int ret;
960         char *aaa;
961
962         if (!cret) return -2;
963         if (!filename) return -2;
964         
965         aaa = (char *)malloc(strlen(filename) + 6);
966         if (!aaa) return -1;
967
968         sprintf(aaa, "DELF %s", filename);
969         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
970         free(aaa);
971         return ret;
972 }
973
974
975 /* MOVF */
976 int CtdlIPCMoveFile(const char *filename, const char *destroom, char *cret)
977 {
978         register int ret;
979         char *aaa;
980
981         if (!cret) return -2;
982         if (!filename) return -2;
983         if (!destroom) return -2;
984
985         aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
986         if (!aaa) return -1;
987
988         sprintf(aaa, "MOVF %s|%s", filename, destroom);
989         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
990         free(aaa);
991         return ret;
992 }
993
994
995 /* NETF */
996 int CtdlIPCNetSendFile(const char *filename, const char *destnode, char *cret)
997 {
998         register int ret;
999         char *aaa;
1000
1001         if (!cret) return -2;
1002         if (!filename) return -2;
1003         if (!destnode) return -2;
1004
1005         aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1006         if (!aaa) return -1;
1007
1008         sprintf(aaa, "NETF %s|%s", filename, destnode);
1009         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1010         free(aaa);
1011         return ret;
1012 }
1013
1014
1015 /* RWHO */
1016 int CtdlIPCOnlineUsers(char **listing, time_t *stamp, char *cret)
1017 {
1018         register int ret;
1019         size_t bytes;
1020
1021         if (!cret) return -1;
1022         if (!listing) return -1;
1023         if (*listing) return -1;
1024
1025         *stamp = CtdlIPCServerTime(cret);
1026         if (!*stamp)
1027                 *stamp = time(NULL);
1028         ret = CtdlIPCGenericCommand("RWHO", NULL, 0, listing, &bytes, cret);
1029         return ret;
1030 }
1031
1032
1033 /* OPEN */
1034 int CtdlIPCFileDownload(const char *filename, void **buf, char *cret)
1035 {
1036         register int ret;
1037         size_t bytes;
1038         time_t last_mod;
1039         char mimetype[SIZ];
1040         char *aaa;
1041
1042         if (!cret) return -2;
1043         if (!filename) return -2;
1044         if (!buf) return -2;
1045         if (*buf) return -2;
1046         if (download_in_progress) return -2;
1047
1048         aaa = (char *)malloc(strlen(filename) + 6);
1049         if (!aaa) return -1;
1050
1051         sprintf(aaa, "OPEN %s", filename);
1052         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1053         free(aaa);
1054         /* FIXME: Possible race condition */
1055         if (ret / 100 == 2) {
1056                 download_in_progress = 1;
1057                 bytes = extract_long(cret, 0);
1058                 last_mod = extract_int(cret, 1);
1059                 extract(mimetype, cret, 2);
1060                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1061                 ret = CtdlIPCEndDownload(cret);
1062                 if (ret / 100 == 2)
1063                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1064                                         filename, mimetype);
1065         }
1066         return ret;
1067 }
1068
1069
1070 /* OPNA */
1071 int CtdlIPCAttachmentDownload(long msgnum, const char *part, void **buf,
1072                 char *cret)
1073 {
1074         register int ret;
1075         size_t bytes;
1076         time_t last_mod;
1077         char filename[SIZ];
1078         char mimetype[SIZ];
1079         char *aaa;
1080
1081         if (!cret) return -2;
1082         if (!buf) return -2;
1083         if (*buf) return -2;
1084         if (!part) return -2;
1085         if (!msgnum) return -2;
1086         if (download_in_progress) return -2;
1087
1088         aaa = (char *)malloc(strlen(part) + 17);
1089         if (!aaa) return -1;
1090
1091         sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1092         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1093         free(aaa);
1094         /* FIXME: Possible race condition */
1095         if (ret / 100 == 2) {
1096                 download_in_progress = 1;
1097                 bytes = extract_long(cret, 0);
1098                 last_mod = extract_int(cret, 1);
1099                 extract(mimetype, cret, 2);
1100                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1101                 ret = CtdlIPCEndDownload(cret);
1102                 if (ret / 100 == 2)
1103                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1104                                         filename, mimetype);
1105         }
1106         return ret;
1107 }
1108
1109
1110 /* OIMG */
1111 int CtdlIPCImageDownload(const char *filename, void **buf, char *cret)
1112 {
1113         register int ret;
1114         size_t bytes;
1115         time_t last_mod;
1116         char mimetype[SIZ];
1117         char *aaa;
1118
1119         if (!cret) return -1;
1120         if (!buf) return -1;
1121         if (*buf) return -1;
1122         if (!filename) return -1;
1123         if (download_in_progress) return -1;
1124
1125         aaa = (char *)malloc(strlen(filename) + 6);
1126         if (!aaa) return -1;
1127
1128         sprintf(aaa, "OIMG %s", filename);
1129         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1130         free(aaa);
1131         /* FIXME: Possible race condition */
1132         if (ret / 100 == 2) {
1133                 download_in_progress = 1;
1134                 bytes = extract_long(cret, 0);
1135                 last_mod = extract_int(cret, 1);
1136                 extract(mimetype, cret, 2);
1137                 ret = CtdlIPCReadDownload(buf, bytes, cret);
1138                 ret = CtdlIPCEndDownload(cret);
1139                 if (ret / 100 == 2)
1140                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1141                                         filename, mimetype);
1142         }
1143         return ret;
1144 }
1145
1146
1147 /* UOPN */
1148 int CtdlIPCFileUpload(const char *filename, const char *comment, void *buf,
1149                 size_t bytes, char *cret)
1150 {
1151         register int ret;
1152         char *aaa;
1153
1154         if (!cret) return -1;
1155         if (!filename) return -1;
1156         if (!comment) return -1;
1157         if (upload_in_progress) return -1;
1158
1159         aaa = (char *)malloc(strlen(filename) + strlen(comment) + 7);
1160         if (!aaa) return -1;
1161
1162         sprintf(aaa, "UOPN %s|%s", filename, comment);
1163         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1164         free(aaa);
1165         /* FIXME: Possible race condition */
1166         if (ret / 100 == 2)
1167                 upload_in_progress = 1;
1168         ret = CtdlIPCWriteUpload(buf, bytes, cret);
1169         ret = CtdlIPCEndUpload(cret);
1170         return ret;
1171 }
1172
1173
1174 /* UIMG */
1175 int CtdlIPCImageUpload(int for_real, const char *filename, size_t bytes,
1176                 char *cret)
1177 {
1178         register int ret;
1179         char *aaa;
1180
1181         if (!cret) return -1;
1182         if (!filename) return -1;
1183         if (upload_in_progress) return -1;
1184
1185         aaa = (char *)malloc(strlen(filename) + 17);
1186         if (!aaa) return -1;
1187
1188         sprintf(aaa, "UIMG %d|%s", for_real, filename);
1189         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1190         free(aaa);
1191         /* FIXME: Possible race condition */
1192         if (ret / 100 == 2)
1193                 upload_in_progress = 1;
1194         return ret;
1195 }
1196
1197
1198 /* QUSR */
1199 int CtdlIPCQueryUsername(const char *username, char *cret)
1200 {
1201         register int ret;
1202         char *aaa;
1203
1204         if (!cret) return -2;
1205         if (!username) return -2;
1206
1207         aaa = (char *)malloc(strlen(username) + 6);
1208         if (!aaa) return -1;
1209
1210         sprintf(aaa, "QUSR %s", username);
1211         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1212         free(aaa);
1213         return ret;
1214 }
1215
1216
1217 /* LFLR */
1218 int CtdlIPCFloorListing(char **listing, char *cret)
1219 {
1220         size_t bytes;
1221
1222         if (!cret) return -2;
1223         if (!listing) return -2;
1224         if (*listing) return -2;
1225
1226         return CtdlIPCGenericCommand("LFLR", NULL, 0, listing, &bytes, cret);
1227 }
1228
1229
1230 /* CFLR */
1231 int CtdlIPCCreateFloor(int for_real, const char *name, char *cret)
1232 {
1233         register int ret;
1234         char *aaa;
1235
1236         if (!cret) return -2;
1237         if (!name) return -2;
1238
1239         aaa = (char *)malloc(strlen(name) + 17);
1240         if (!aaa) return -1;
1241
1242         sprintf(aaa, "CFLR %s|%d", name, for_real);
1243         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1244         free(aaa);
1245         return ret;
1246 }
1247
1248
1249 /* KFLR */
1250 int CtdlIPCDeleteFloor(int for_real, int floornum, char *cret)
1251 {
1252         char aaa[27];
1253
1254         if (!cret) return -1;
1255         if (floornum < 0) return -1;
1256
1257         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1258         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1259 }
1260
1261
1262 /* EFLR */
1263 int CtdlIPCEditFloor(int floornum, const char *floorname, char *cret)
1264 {
1265         register int ret;
1266         char *aaa;
1267
1268         if (!cret) return -2;
1269         if (!floorname) return -2;
1270         if (floornum < 0) return -2;
1271
1272         aaa = (char *)malloc(strlen(floorname) + 17);
1273         if (!aaa) return -1;
1274
1275         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1276         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1277         free(aaa);
1278         return ret;
1279 }
1280
1281
1282 /* IDEN */
1283 int CtdlIPCIdentifySoftware(int developerid, int clientid, int revision,
1284                 const char *software_name, const char *hostname, char *cret)
1285 {
1286         register int ret;
1287         char *aaa;
1288
1289         if (developerid < 0) return -2;
1290         if (clientid < 0) return -2;
1291         if (revision < 0) return -2;
1292         if (!software_name) return -2;
1293         if (!hostname) return -2;
1294
1295         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1296         if (!aaa) return -1;
1297
1298         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1299                         revision, software_name, hostname);
1300         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1301         free(aaa);
1302         return ret;
1303 }
1304
1305
1306 /* SEXP */
1307 int CtdlIPCSendInstantMessage(const char *username, const char *text,
1308                 char *cret)
1309 {
1310         register int ret;
1311         char *aaa;
1312
1313         if (!cret) return -2;
1314         if (!username) return -2;
1315
1316         aaa = (char *)malloc(strlen(username) + 8);
1317         if (!aaa) return -1;
1318
1319         if (text) {
1320                 sprintf(aaa, "SEXP %s|-", username);
1321                 ret = CtdlIPCGenericCommand(aaa, text, strlen(text),
1322                                 NULL, NULL, cret);
1323         } else {
1324                 sprintf(aaa, "SEXP %s||", username);
1325                 ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1326         }
1327         free(aaa);
1328         return ret;
1329 }
1330
1331
1332 /* GEXP */
1333 int CtdlIPCGetInstantMessage(char **listing, char *cret)
1334 {
1335         size_t bytes;
1336
1337         if (!cret) return -2;
1338         if (!listing) return -2;
1339         if (*listing) return -2;
1340
1341         return CtdlIPCGenericCommand("GEXP", NULL, 0, listing, &bytes, cret);
1342 }
1343
1344
1345 /* DEXP */
1346 /* mode is 0 = enable, 1 = disable, 2 = status */
1347 int CtdlIPCEnableInstantMessageReceipt(int mode, char *cret)
1348 {
1349         char aaa[16];
1350
1351         if (!cret) return -2;
1352
1353         sprintf(aaa, "DEXP %d", mode);
1354         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1355 }
1356
1357
1358 /* EBIO */
1359 int CtdlIPCSetBio(char *bio, char *cret)
1360 {
1361         if (!cret) return -2;
1362         if (!bio) return -2;
1363
1364         return CtdlIPCGenericCommand("EBIO", bio, strlen(bio),
1365                         NULL, NULL, cret);
1366 }
1367
1368
1369 /* RBIO */
1370 int CtdlIPCGetBio(const char *username, char **listing, char *cret)
1371 {
1372         register int ret;
1373         size_t bytes;
1374         char *aaa;
1375
1376         if (!cret) return -2;
1377         if (!username) return -2;
1378         if (!listing) return -2;
1379         if (*listing) return -2;
1380
1381         aaa = (char *)malloc(strlen(username) + 6);
1382         if (!aaa) return -1;
1383
1384         sprintf(aaa, "RBIO %s", username);
1385         ret = CtdlIPCGenericCommand(aaa, NULL, 0, listing, &bytes, cret);
1386         free(aaa);
1387         return ret;
1388 }
1389
1390
1391 /* LBIO */
1392 int CtdlIPCListUsersWithBios(char **listing, char *cret)
1393 {
1394         size_t bytes;
1395
1396         if (!cret) return -2;
1397         if (!listing) return -2;
1398         if (*listing) return -2;
1399
1400         return CtdlIPCGenericCommand("LBIO", NULL, 0, listing, &bytes, cret);
1401 }
1402
1403
1404 /* STEL */
1405 int CtdlIPCStealthMode(int mode, char *cret)
1406 {
1407         char aaa[16];
1408
1409         if (!cret) return -1;
1410
1411         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1412         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1413 }
1414
1415
1416 /* TERM */
1417 int CtdlIPCTerminateSession(int sid, char *cret)
1418 {
1419         char aaa[16];
1420
1421         if (!cret) return -1;
1422
1423         sprintf(aaa, "TERM %d", sid);
1424         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1425 }
1426
1427
1428 /* DOWN */
1429 int CtdlIPCTerminateServerNow(char *cret)
1430 {
1431         if (!cret) return -1;
1432
1433         return CtdlIPCGenericCommand("DOWN", NULL, 0, NULL, NULL, cret);
1434 }
1435
1436
1437 /* SCDN */
1438 int CtdlIPCTerminateServerScheduled(int mode, char *cret)
1439 {
1440         char aaa[16];
1441
1442         if (!cret) return -1;
1443
1444         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1445         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1446 }
1447
1448
1449 /* EMSG */
1450 int CtdlIPCEnterSystemMessage(const char *filename, const char *text,
1451                 char *cret)
1452 {
1453         register int ret;
1454         char *aaa;
1455
1456         if (!cret) return -2;
1457         if (!text) return -2;
1458         if (!filename) return -2;
1459
1460         aaa = (char *)malloc(strlen(filename) + 6);
1461         if (!aaa) return -1;
1462
1463         sprintf(aaa, "EMSG %s", filename);
1464         ret = CtdlIPCGenericCommand(aaa, text, strlen(text), NULL, NULL, cret);
1465         free(aaa);
1466         return ret;
1467 }
1468
1469
1470 /* HCHG */
1471 int CtdlIPCChangeHostname(const char *hostname, char *cret)
1472 {
1473         register int ret;
1474         char *aaa;
1475
1476         if (!cret) return -2;
1477         if (!hostname) return -2;
1478
1479         aaa = (char *)malloc(strlen(hostname) + 6);
1480         if (!aaa) return -1;
1481
1482         sprintf(aaa, "HCHG %s", hostname);
1483         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1484         free(aaa);
1485         return ret;
1486 }
1487
1488
1489 /* RCHG */
1490 int CtdlIPCChangeRoomname(const char *roomname, char *cret)
1491 {
1492         register int ret;
1493         char *aaa;
1494
1495         if (!cret) return -2;
1496         if (!roomname) return -2;
1497
1498         aaa = (char *)malloc(strlen(roomname) + 6);
1499         if (!aaa) return -1;
1500
1501         sprintf(aaa, "RCHG %s", roomname);
1502         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1503         free(aaa);
1504         return ret;
1505 }
1506
1507
1508 /* UCHG */
1509 int CtdlIPCChangeUsername(const char *username, char *cret)
1510 {
1511         register int ret;
1512         char *aaa;
1513
1514         if (!cret) return -2;
1515         if (!username) return -2;
1516
1517         aaa = (char *)malloc(strlen(username) + 6);
1518         if (!aaa) return -1;
1519
1520         sprintf(aaa, "UCHG %s", username);
1521         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1522         free(aaa);
1523         return ret;
1524 }
1525
1526
1527 /* TIME */
1528 /* This function returns the actual server time reported, or 0 if error */
1529 time_t CtdlIPCServerTime(char *cret)
1530 {
1531         register time_t tret;
1532         register int ret;
1533
1534         ret = CtdlIPCGenericCommand("TIME", NULL, 0, NULL, NULL, cret);
1535         if (ret / 100 == 2) {
1536                 tret = extract_long(cret, 0);
1537         } else {
1538                 tret = 0L;
1539         }
1540         return tret;
1541 }
1542
1543
1544 /* AGUP */
1545 int CtdlIPCAideGetUserParameters(const char *who,
1546                                  struct usersupp **uret, char *cret)
1547 {
1548         register int ret;
1549         char *aaa;
1550
1551         if (!cret) return -2;
1552         if (!uret) return -2;
1553         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof(struct usersupp));
1554         if (!*uret) return -1;
1555
1556         aaa = (char *)malloc(strlen(uret[0]->fullname) + 6);
1557         if (!aaa) return -1;
1558
1559         sprintf(aaa, "AGUP %s", who);
1560         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1561         if (ret / 100 == 2) {
1562                 extract(uret[0]->fullname, cret, 0);
1563                 extract(uret[0]->password, cret, 1);
1564                 uret[0]->flags = extract_int(cret, 2);
1565                 uret[0]->timescalled = extract_long(cret, 3);
1566                 uret[0]->posted = extract_long(cret, 4);
1567                 uret[0]->axlevel = extract_int(cret, 5);
1568                 uret[0]->usernum = extract_long(cret, 6);
1569                 uret[0]->lastcall = extract_long(cret, 7);
1570                 uret[0]->USuserpurge = extract_int(cret, 8);
1571         }
1572         free(aaa);
1573         return ret;
1574 }
1575
1576
1577 /* ASUP */
1578 int CtdlIPCAideSetUserParameters(const struct usersupp *uret, char *cret)
1579 {
1580         register int ret;
1581         char *aaa;
1582
1583         if (!cret) return -2;
1584         if (!uret) return -2;
1585
1586         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1587         if (!aaa) return -1;
1588
1589         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1590                         uret->fullname, uret->password, uret->flags,
1591                         uret->timescalled, uret->posted, uret->axlevel,
1592                         uret->usernum, uret->lastcall, uret->USuserpurge);
1593         ret = CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1594         free(aaa);
1595         return ret;
1596 }
1597
1598
1599 /* GPEX */
1600 /* which is 0 = room, 1 = floor, 2 = site */
1601 int CtdlIPCGetMessageExpirationPolicy(int which, char *cret)
1602 {
1603         static char *proto[] = {"room", "floor", "site"};
1604         char aaa[11];
1605
1606         if (!cret) return -2;
1607         if (which < 0 || which > 2) return -2;
1608         
1609         sprintf(aaa, "GPEX %s", proto[which]);
1610         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1611 }
1612
1613
1614 /* SPEX */
1615 /* which is 0 = room, 1 = floor, 2 = site */
1616 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1617 int CtdlIPCSetMessageExpirationPolicy(int which, int policy, int value,
1618                 char *cret)
1619 {
1620         char aaa[38];
1621
1622         if (!cret) return -2;
1623         if (which < 0 || which > 2) return -2;
1624         if (policy < 0 || policy > 3) return -2;
1625         if (policy >= 2 && value < 1) return -2;
1626
1627         sprintf(aaa, "SPEX %d|%d|%d", which, policy, value);
1628         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1629 }
1630
1631
1632 /* CONF GET */
1633 int CtdlGetSystemConfig(char **listing, char *cret)
1634 {
1635         size_t bytes;
1636
1637         if (!cret) return -2;
1638         if (!listing) return -2;
1639         if (*listing) return -2;
1640
1641         return CtdlIPCGenericCommand("CONF GET", NULL, 0,
1642                         listing, &bytes, cret);
1643 }
1644
1645
1646 /* CONF SET */
1647 int CtdlSetSystemConfig(const char *listing, char *cret)
1648 {
1649         if (!cret) return -2;
1650         if (!listing) return -2;
1651
1652         return CtdlIPCGenericCommand("CONF SET", listing, strlen(listing),
1653                         NULL, NULL, cret);
1654 }
1655
1656
1657 /* MMOD */
1658 int CtdlIPCModerateMessage(long msgnum, int level, char *cret)
1659 {
1660         char aaa[27];
1661
1662         if (!cret) return -2;
1663         if (!msgnum) return -2;
1664
1665         sprintf(aaa, "MMOD %ld|%d", msgnum, level);
1666         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1667 }
1668
1669
1670 /* REQT */
1671 int CtdlIPCRequestClientLogout(int session, char *cret)
1672 {
1673         char aaa[16];
1674
1675         if (!cret) return -2;
1676         if (session < 0) return -2;
1677
1678         sprintf(aaa, "REQT %d", session);
1679         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1680 }
1681
1682
1683 /* SEEN */
1684 int CtdlIPCSetMessageSeen(long msgnum, int seen, char *cret)
1685 {
1686         char aaa[27];
1687
1688         if (!cret) return -2;
1689         if (msgnum < 0) return -2;
1690
1691         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1692         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1693 }
1694
1695
1696 /* STLS */
1697 int CtdlIPCStartEncryption(char *cret)
1698 {
1699         return CtdlIPCGenericCommand("STLS", NULL, 0, NULL, NULL, cret);
1700 }
1701
1702
1703 /* QDIR */
1704 int CtdlIPCDirectoryLookup(const char *address, char *cret)
1705 {
1706         char *aaa;
1707
1708         if (!address) return -2;
1709         if (!cret) return -2;
1710
1711         aaa = (char *)malloc(strlen(address) + 6);
1712         if (!aaa) return -1;
1713
1714         sprintf(aaa, "QDIR %s", address);
1715         return CtdlIPCGenericCommand(aaa, NULL, 0, NULL, NULL, cret);
1716 }
1717
1718
1719 /*
1720  * Not implemented:
1721  * 
1722  * CHAT
1723  * ETLS
1724  * EXPI
1725  * GTLS
1726  * IGAB
1727  * IPGM
1728  * MSG3
1729  * MSG4
1730  * NDOP
1731  * NETP
1732  * NUOP
1733  * SMTP
1734  */
1735
1736
1737 /* ************************************************************************** */
1738 /*             Stuff below this line is not for public consumption            */
1739 /* ************************************************************************** */
1740
1741
1742 inline void netio_lock(void)
1743 {
1744 #ifdef THREADED_CLIENT
1745         pthread_mutex_lock(&rwlock);
1746 #endif
1747 }
1748
1749
1750 inline void netio_unlock(void)
1751 {
1752 #ifdef THREADED_CLIENT
1753         pthread_mutex_unlock(&rwlock);
1754 #endif
1755 }
1756
1757
1758 /* Read a listing from the server up to 000.  Append to dest if it exists */
1759 char *CtdlIPCReadListing(char *dest)
1760 {
1761         long length = 0;
1762         char *ret;
1763         char aaa[SIZ];
1764
1765         ret = dest;
1766         if (ret) length = strlen(ret);
1767         while (serv_gets(aaa), strcmp(aaa, "000")) {
1768                 ret = (char *)realloc(ret, length + strlen(aaa) + 2);
1769                 if (ret) {
1770                         strcpy(&ret[length], aaa);
1771                         length += strlen(aaa);
1772                         strcpy(&ret[length++], "\n");
1773                 }
1774         }
1775         return ret;
1776 }
1777
1778
1779 /* Send a listing to the server; generate the ending 000. */
1780 int CtdlIPCSendListing(const char *listing)
1781 {
1782         char *text;
1783
1784         text = (char *)malloc(strlen(listing) + 6);
1785         if (text) {
1786                 strcpy(text, listing);
1787                 while (text[strlen(text) - 1] == '\n')
1788                         text[strlen(text) - 1] = '\0';
1789                 strcat(text, "\n000");
1790                 serv_puts(text);
1791                 free(text);
1792                 text = NULL;
1793         } else {
1794                 /* Malloc failed but we are committed to send */
1795                 /* This may result in extra blanks at the bottom */
1796                 serv_puts(text);
1797                 serv_puts("000");
1798         }
1799         return 0;
1800 }
1801
1802
1803 /* Partial read of file from server */
1804 size_t CtdlIPCPartialRead(void **buf, size_t offset, size_t bytes, char *cret)
1805 {
1806         register size_t len = 0;
1807         char aaa[SIZ];
1808
1809         if (!buf) return -1;
1810         if (!cret) return -1;
1811         if (bytes < 1) return -1;
1812         if (offset < 0) return -1;
1813
1814         netio_lock();
1815         sprintf(aaa, "READ %d|%d", offset, bytes);
1816         serv_puts(aaa);
1817         serv_gets(aaa);
1818         if (aaa[0] != '6')
1819                 strcpy(cret, &aaa[4]);
1820         else {
1821                 len = extract_long(&aaa[4], 0);
1822                 *buf = (void *)realloc(*buf, offset + len);
1823                 if (*buf) {
1824                         /* I know what I'm doing */
1825                         serv_read((char *)&buf[offset], len);
1826                 } else {
1827                         /* We have to read regardless */
1828                         serv_read(aaa, len);
1829                         len = -1;
1830                 }
1831         }
1832         netio_unlock();
1833         return len;
1834 }
1835
1836
1837 /* CLOS */
1838 int CtdlIPCEndDownload(char *cret)
1839 {
1840         register int ret;
1841
1842         if (!cret) return -2;
1843         if (!download_in_progress) return -2;
1844
1845         ret = CtdlIPCGenericCommand("CLOS", NULL, 0, NULL, NULL, cret);
1846         if (ret / 100 == 2)
1847                 download_in_progress = 0;
1848         return ret;
1849 }
1850
1851
1852 /* READ */
1853 int CtdlIPCReadDownload(void **buf, size_t bytes, char *cret)
1854 {
1855         register size_t len;
1856
1857         if (!cret) return -1;
1858         if (!buf) return -1;
1859         if (*buf) return -1;
1860         if (!download_in_progress) return -1;
1861
1862         len = 0;
1863         while (len < bytes) {
1864                 len = CtdlIPCPartialRead(buf, len, 4096, cret);
1865                 if (len == -1) {
1866                         free(*buf);
1867                         return 0;
1868                 }
1869         }
1870         return len;
1871 }
1872
1873
1874 /* UCLS */
1875 int CtdlIPCEndUpload(char *cret)
1876 {
1877         register int ret;
1878
1879         if (!cret) return -1;
1880         if (!upload_in_progress) return -1;
1881
1882         ret = CtdlIPCGenericCommand("UCLS", NULL, 0, NULL, NULL, cret);
1883         if (ret / 100 == 2)
1884                 upload_in_progress = 0;
1885         return ret;
1886 }
1887
1888
1889 /* WRIT */
1890 int CtdlIPCWriteUpload(void *buf, size_t bytes, char *cret)
1891 {
1892         register int ret = -1;
1893         register size_t offset;
1894         char aaa[SIZ];
1895
1896         if (!cret) return -1;
1897         if (!buf) return -1;
1898         if (bytes < 1) return -1;
1899
1900         offset = 0;
1901         while (offset < bytes) {
1902                 sprintf(aaa, "WRIT %d", bytes - offset);
1903                 serv_puts(aaa);
1904                 serv_gets(aaa);
1905                 strcpy(cret, &aaa[4]);
1906                 ret = atoi(aaa);
1907                 if (aaa[0] == '7') {
1908                         register size_t to_write;
1909
1910                         to_write = extract_long(&aaa[4], 0);
1911                         serv_write(buf + offset, to_write);
1912                         offset += to_write;
1913                 } else {
1914                         break;
1915                 }
1916         }
1917         return ret;
1918 }
1919
1920
1921 /*
1922  * Generic command method.  This method should handle any server command
1923  * except for CHAT.  It takes the following arguments:
1924  *
1925  * command              Preformatted command to send to server
1926  * to_send              A text or binary file to send to server
1927  *                      (only sent if server requests it)
1928  * bytes_to_send        The number of bytes in to_send (required if
1929  *                      sending binary, optional if sending listing)
1930  * to_receive           Pointer to a NULL pointer, if the server
1931  *                      sends text or binary we will allocate memory
1932  *                      for the file and stuff it here
1933  * bytes_to_receive     If a file is received, we will store its
1934  *                      byte count here
1935  * proto_response       The protocol response.  Caller must provide
1936  *                      this buffer and ensure that it is at least
1937  *                      128 bytes in length.
1938  *
1939  * This function returns a number equal to the protocol response number,
1940  * -1 if an internal error occurred, -2 if caller provided bad values,
1941  * or 0 - the protocol response number if bad values were found during
1942  * the protocol exchange.
1943  * It stores the protocol response string (minus the number) in 
1944  * protocol_response as described above.  Some commands send additional
1945  * data in this string.
1946  */
1947 int CtdlIPCGenericCommand(const char *command, const char *to_send,
1948                 size_t bytes_to_send, char **to_receive, 
1949                 size_t *bytes_to_receive, char *proto_response)
1950 {
1951         char buf[SIZ];
1952         register int ret;
1953
1954         if (!command) return -2;
1955         if (!proto_response) return -2;
1956
1957         netio_lock();
1958         serv_puts((char *)command);
1959         while (1) {
1960                 serv_gets(proto_response);
1961                 if (proto_response[3] == '*')
1962                         express_msgs = 1;
1963                 ret = atoi(proto_response);
1964                 memmove(proto_response, &proto_response[4],
1965                                 strlen(proto_response) - 3);
1966                 switch (ret / 100) {
1967                 default:                        /* Unknown, punt */
1968                 case 2:                         /* OK */
1969                 case 3:                         /* MORE_DATA */
1970                 case 5:                         /* ERROR */
1971                         /* Don't need to do anything */
1972                         break;
1973                 case 1:                         /* LISTING_FOLLOWS */
1974                         if (to_receive && !*to_receive && bytes_to_receive) {
1975                                 *to_receive = CtdlIPCReadListing(NULL);
1976                         } else { /* Drain */
1977                                 while (serv_gets(buf), strcmp(buf, "000")) ;
1978                                 ret = -ret;
1979                         }
1980                         break;
1981                 case 4:                         /* SEND_LISTING */
1982                         if (to_send) {
1983                                 CtdlIPCSendListing(to_send);
1984                         } else {
1985                                 /* No listing given, fake it */
1986                                 serv_puts("000");
1987                                 ret = -ret;
1988                         }
1989                         break;
1990                 case 6:                         /* BINARY_FOLLOWS */
1991                         if (to_receive && !*to_receive && bytes_to_receive) {
1992                                 *bytes_to_receive =
1993                                         extract_long(proto_response, 0);
1994                                 *to_receive = (char *)malloc(*bytes_to_receive);
1995                                 if (!*to_receive) {
1996                                         ret = -1;
1997                                 } else {
1998                                         serv_read(*to_receive,
1999                                                         *bytes_to_receive);
2000                                 }
2001                         } else {
2002                                 /* Drain */
2003                                 size_t drain;
2004
2005                                 drain = extract_long(proto_response, 0);
2006                                 while (drain > SIZ) {
2007                                         serv_read(buf, SIZ);
2008                                         drain -= SIZ;
2009                                 }
2010                                 serv_read(buf, drain);
2011                                 ret = -ret;
2012                         }
2013                         break;
2014                 case 7:                         /* SEND_BINARY */
2015                         if (to_send && bytes_to_send) {
2016                                 serv_write((char *)to_send, bytes_to_send);
2017                         } else if (bytes_to_send) {
2018                                 /* Fake it, send nulls */
2019                                 size_t fake;
2020
2021                                 fake = bytes_to_send;
2022                                 memset(buf, '\0', SIZ);
2023                                 while (fake > SIZ) {
2024                                         serv_write(buf, SIZ);
2025                                         fake -= SIZ;
2026                                 }
2027                                 serv_write(buf, fake);
2028                                 ret = -ret;
2029                         } /* else who knows?  DANGER WILL ROBINSON */
2030                         break;
2031                 case 8:                         /* START_CHAT_MODE */
2032                         if (!strncasecmp(command, "CHAT", 4)) {
2033                                 /* Don't call chatmode with generic! */
2034                                 serv_puts("/quit");
2035                                 ret = -ret;
2036                         } else {
2037                                 /* In this mode we send then receive listing */
2038                                 if (to_send) {
2039                                         CtdlIPCSendListing(to_send);
2040                                 } else {
2041                                         /* No listing given, fake it */
2042                                         serv_puts("000");
2043                                         ret = -ret;
2044                                 }
2045                                 if (to_receive && !*to_receive
2046                                                 && bytes_to_receive) {
2047                                         *to_receive = CtdlIPCReadListing(NULL);
2048                                 } else { /* Drain */
2049                                         while (serv_gets(buf),
2050                                                         strcmp(buf, "000")) ;
2051                                         ret = -ret;
2052                                 }
2053                         }
2054                         break;
2055                 case 9:                         /* ASYNC_MSG */
2056                         /* CtdlIPCDoAsync(ret, proto_response); */
2057                         free(CtdlIPCReadListing(NULL)); /* STUB FIXME */
2058                         break;
2059                 }
2060                 if (ret / 100 != 9)
2061                         break;
2062         }
2063         netio_unlock();
2064         return ret;
2065 }