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