]> code.citadel.org Git - citadel.git/blob - citadel/citadel_ipc.c
* File/image uploads now implemented in terms of 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 *save_as, const char *comment,
1163                 const char *path, void (*progress_gauge_callback)(long, long),
1164                 char *cret)
1165 {
1166         register int ret;
1167         char *aaa;
1168
1169         if (!cret) return -1;
1170         if (!save_as) return -1;
1171         if (!comment) return -1;
1172         if (!path) return -1;
1173         if (!*path) return -1;
1174         if (ipc->uploading) return -1;
1175
1176         aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1177         if (!aaa) return -1;
1178
1179         sprintf(aaa, "UOPN %s|%s", save_as, comment);
1180         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1181         free(aaa);
1182         if (ret / 100 == 2) {
1183                 ipc->uploading = 1;
1184                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1185                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1186                 ipc->uploading = 0;
1187         }
1188         return ret;
1189 }
1190
1191
1192 /* UIMG */
1193 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1194                 const char *save_as,
1195                 void (*progress_gauge_callback)(long, long), char *cret)
1196 {
1197         register int ret;
1198         char *aaa;
1199
1200         if (!cret) return -1;
1201         if (!save_as) return -1;
1202         if (!path && for_real) return -1;
1203         if (!*path && for_real) return -1;
1204         if (ipc->uploading) return -1;
1205
1206         aaa = (char *)malloc(strlen(save_as) + 17);
1207         if (!aaa) return -1;
1208
1209         sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1210         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1211         free(aaa);
1212         if (ret / 100 == 2 && for_real) {
1213                 ipc->uploading = 1;
1214                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1215                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1216                 ipc->uploading = 0;
1217         }
1218         return ret;
1219 }
1220
1221
1222 /* QUSR */
1223 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1224 {
1225         register int ret;
1226         char *aaa;
1227
1228         if (!cret) return -2;
1229         if (!username) return -2;
1230
1231         aaa = (char *)malloc(strlen(username) + 6);
1232         if (!aaa) return -1;
1233
1234         sprintf(aaa, "QUSR %s", username);
1235         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1236         free(aaa);
1237         return ret;
1238 }
1239
1240
1241 /* LFLR */
1242 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1243 {
1244         size_t bytes;
1245
1246         if (!cret) return -2;
1247         if (!listing) return -2;
1248         if (*listing) return -2;
1249
1250         return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1251 }
1252
1253
1254 /* CFLR */
1255 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1256 {
1257         register int ret;
1258         char *aaa;
1259
1260         if (!cret) return -2;
1261         if (!name) return -2;
1262
1263         aaa = (char *)malloc(strlen(name) + 17);
1264         if (!aaa) return -1;
1265
1266         sprintf(aaa, "CFLR %s|%d", name, for_real);
1267         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1268         free(aaa);
1269         return ret;
1270 }
1271
1272
1273 /* KFLR */
1274 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1275 {
1276         char aaa[27];
1277
1278         if (!cret) return -1;
1279         if (floornum < 0) return -1;
1280
1281         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1282         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1283 }
1284
1285
1286 /* EFLR */
1287 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1288 {
1289         register int ret;
1290         char *aaa;
1291
1292         if (!cret) return -2;
1293         if (!floorname) return -2;
1294         if (floornum < 0) return -2;
1295
1296         aaa = (char *)malloc(strlen(floorname) + 17);
1297         if (!aaa) return -1;
1298
1299         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1300         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1301         free(aaa);
1302         return ret;
1303 }
1304
1305
1306 /* IDEN */
1307 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid, int revision,
1308                 const char *software_name, const char *hostname, char *cret)
1309 {
1310         register int ret;
1311         char *aaa;
1312
1313         if (developerid < 0) return -2;
1314         if (clientid < 0) return -2;
1315         if (revision < 0) return -2;
1316         if (!software_name) return -2;
1317         if (!hostname) return -2;
1318
1319         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1320         if (!aaa) return -1;
1321
1322         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1323                         revision, software_name, hostname);
1324         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1325         free(aaa);
1326         return ret;
1327 }
1328
1329
1330 /* SEXP */
1331 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1332                 char *cret)
1333 {
1334         register int ret;
1335         char *aaa;
1336
1337         if (!cret) return -2;
1338         if (!username) return -2;
1339
1340         aaa = (char *)malloc(strlen(username) + 8);
1341         if (!aaa) return -1;
1342
1343         if (text) {
1344                 sprintf(aaa, "SEXP %s|-", username);
1345                 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1346                                 NULL, NULL, cret);
1347         } else {
1348                 sprintf(aaa, "SEXP %s||", username);
1349                 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1350         }
1351         free(aaa);
1352         return ret;
1353 }
1354
1355
1356 /* GEXP */
1357 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1358 {
1359         size_t bytes;
1360
1361         if (!cret) return -2;
1362         if (!listing) return -2;
1363         if (*listing) return -2;
1364
1365         return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1366 }
1367
1368
1369 /* DEXP */
1370 /* mode is 0 = enable, 1 = disable, 2 = status */
1371 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1372 {
1373         char aaa[16];
1374
1375         if (!cret) return -2;
1376
1377         sprintf(aaa, "DEXP %d", mode);
1378         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1379 }
1380
1381
1382 /* EBIO */
1383 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1384 {
1385         if (!cret) return -2;
1386         if (!bio) return -2;
1387
1388         return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1389                         NULL, NULL, cret);
1390 }
1391
1392
1393 /* RBIO */
1394 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1395 {
1396         register int ret;
1397         size_t bytes;
1398         char *aaa;
1399
1400         if (!cret) return -2;
1401         if (!username) return -2;
1402         if (!listing) return -2;
1403         if (*listing) return -2;
1404
1405         aaa = (char *)malloc(strlen(username) + 6);
1406         if (!aaa) return -1;
1407
1408         sprintf(aaa, "RBIO %s", username);
1409         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1410         free(aaa);
1411         return ret;
1412 }
1413
1414
1415 /* LBIO */
1416 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1417 {
1418         size_t bytes;
1419
1420         if (!cret) return -2;
1421         if (!listing) return -2;
1422         if (*listing) return -2;
1423
1424         return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1425 }
1426
1427
1428 /* STEL */
1429 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1430 {
1431         char aaa[16];
1432
1433         if (!cret) return -1;
1434
1435         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1436         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1437 }
1438
1439
1440 /* TERM */
1441 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1442 {
1443         char aaa[16];
1444
1445         if (!cret) return -1;
1446
1447         sprintf(aaa, "TERM %d", sid);
1448         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1449 }
1450
1451
1452 /* DOWN */
1453 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1454 {
1455         if (!cret) return -1;
1456
1457         return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1458 }
1459
1460
1461 /* SCDN */
1462 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1463 {
1464         char aaa[16];
1465
1466         if (!cret) return -1;
1467
1468         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1469         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1470 }
1471
1472
1473 /* EMSG */
1474 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1475                 char *cret)
1476 {
1477         register int ret;
1478         char *aaa;
1479
1480         if (!cret) return -2;
1481         if (!text) return -2;
1482         if (!filename) return -2;
1483
1484         aaa = (char *)malloc(strlen(filename) + 6);
1485         if (!aaa) return -1;
1486
1487         sprintf(aaa, "EMSG %s", filename);
1488         ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1489         free(aaa);
1490         return ret;
1491 }
1492
1493
1494 /* HCHG */
1495 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1496 {
1497         register int ret;
1498         char *aaa;
1499
1500         if (!cret) return -2;
1501         if (!hostname) return -2;
1502
1503         aaa = (char *)malloc(strlen(hostname) + 6);
1504         if (!aaa) return -1;
1505
1506         sprintf(aaa, "HCHG %s", hostname);
1507         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1508         free(aaa);
1509         return ret;
1510 }
1511
1512
1513 /* RCHG */
1514 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1515 {
1516         register int ret;
1517         char *aaa;
1518
1519         if (!cret) return -2;
1520         if (!roomname) return -2;
1521
1522         aaa = (char *)malloc(strlen(roomname) + 6);
1523         if (!aaa) return -1;
1524
1525         sprintf(aaa, "RCHG %s", roomname);
1526         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1527         free(aaa);
1528         return ret;
1529 }
1530
1531
1532 /* UCHG */
1533 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1534 {
1535         register int ret;
1536         char *aaa;
1537
1538         if (!cret) return -2;
1539         if (!username) return -2;
1540
1541         aaa = (char *)malloc(strlen(username) + 6);
1542         if (!aaa) return -1;
1543
1544         sprintf(aaa, "UCHG %s", username);
1545         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1546         free(aaa);
1547         return ret;
1548 }
1549
1550
1551 /* TIME */
1552 /* This function returns the actual server time reported, or 0 if error */
1553 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1554 {
1555         register time_t tret;
1556         register int ret;
1557
1558         ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1559         if (ret / 100 == 2) {
1560                 tret = extract_long(cret, 0);
1561         } else {
1562                 tret = 0L;
1563         }
1564         return tret;
1565 }
1566
1567
1568 /* AGUP */
1569 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1570                                  struct usersupp **uret, char *cret)
1571 {
1572         register int ret;
1573         char aaa[SIZ];
1574
1575         if (!cret) return -2;
1576         if (!uret) return -2;
1577         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof(struct usersupp));
1578         if (!*uret) return -1;
1579
1580         sprintf(aaa, "AGUP %s", who);
1581         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1582
1583         if (ret / 100 == 2) {
1584                 extract(uret[0]->fullname, cret, 0);
1585                 extract(uret[0]->password, cret, 1);
1586                 uret[0]->flags = extract_int(cret, 2);
1587                 uret[0]->timescalled = extract_long(cret, 3);
1588                 uret[0]->posted = extract_long(cret, 4);
1589                 uret[0]->axlevel = extract_int(cret, 5);
1590                 uret[0]->usernum = extract_long(cret, 6);
1591                 uret[0]->lastcall = extract_long(cret, 7);
1592                 uret[0]->USuserpurge = extract_int(cret, 8);
1593         }
1594         return ret;
1595 }
1596
1597
1598 /* ASUP */
1599 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct usersupp *uret, char *cret)
1600 {
1601         register int ret;
1602         char *aaa;
1603
1604         if (!cret) return -2;
1605         if (!uret) return -2;
1606
1607         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1608         if (!aaa) return -1;
1609
1610         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1611                         uret->fullname, uret->password, uret->flags,
1612                         uret->timescalled, uret->posted, uret->axlevel,
1613                         uret->usernum, uret->lastcall, uret->USuserpurge);
1614         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1615         free(aaa);
1616         return ret;
1617 }
1618
1619
1620 /* GPEX */
1621 /* which is 0 = room, 1 = floor, 2 = site */
1622 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which, char *cret)
1623 {
1624         static char *proto[] = {"room", "floor", "site"};
1625         char aaa[11];
1626
1627         if (!cret) return -2;
1628         if (which < 0 || which > 2) return -2;
1629         
1630         sprintf(aaa, "GPEX %s", proto[which]);
1631         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1632 }
1633
1634
1635 /* SPEX */
1636 /* which is 0 = room, 1 = floor, 2 = site */
1637 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1638 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which, int policy, int value,
1639                 char *cret)
1640 {
1641         char aaa[38];
1642
1643         if (!cret) return -2;
1644         if (which < 0 || which > 2) return -2;
1645         if (policy < 0 || policy > 3) return -2;
1646         if (policy >= 2 && value < 1) return -2;
1647
1648         sprintf(aaa, "SPEX %d|%d|%d", which, policy, value);
1649         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1650 }
1651
1652
1653 /* CONF GET */
1654 int CtdlGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1655 {
1656         size_t bytes;
1657
1658         if (!cret) return -2;
1659         if (!listing) return -2;
1660         if (*listing) return -2;
1661
1662         return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1663                         listing, &bytes, cret);
1664 }
1665
1666
1667 /* CONF SET */
1668 int CtdlSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1669 {
1670         if (!cret) return -2;
1671         if (!listing) return -2;
1672
1673         return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1674                         NULL, NULL, cret);
1675 }
1676
1677
1678 /* CONF GETSYS */
1679 int CtdlGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1680                 char **listing, char *cret)
1681 {
1682         char *aaa;
1683         size_t bytes;
1684
1685         if (!cret) return -2;
1686         if (!mimetype) return -2;
1687         if (!listing) return -2;
1688         if (*listing) return -2;
1689
1690         aaa = malloc(strlen(mimetype) + 13);
1691         if (!aaa) return -1;
1692         sprintf(aaa, "CONF GETSYS|%s", mimetype);
1693         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1694                         listing, &bytes, cret);
1695 }
1696
1697
1698 /* CONF PUTSYS */
1699 int CtdlSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1700                const char *listing, char *cret)
1701 {
1702         char *aaa;
1703
1704         if (!cret) return -2;
1705         if (!mimetype) return -2;
1706         if (!listing) return -2;
1707
1708         aaa = malloc(strlen(mimetype) + 13);
1709         if (!aaa) return -1;
1710         sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1711         return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1712                         NULL, NULL, cret);
1713 }
1714
1715 /* MMOD */
1716 int CtdlIPCModerateMessage(CtdlIPC *ipc, long msgnum, int level, char *cret)
1717 {
1718         char aaa[27];
1719
1720         if (!cret) return -2;
1721         if (!msgnum) return -2;
1722
1723         sprintf(aaa, "MMOD %ld|%d", msgnum, level);
1724         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1725 }
1726
1727
1728 /* REQT */
1729 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1730 {
1731         char aaa[16];
1732
1733         if (!cret) return -2;
1734         if (session < 0) return -2;
1735
1736         sprintf(aaa, "REQT %d", session);
1737         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1738 }
1739
1740
1741 /* SEEN */
1742 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1743 {
1744         char aaa[27];
1745
1746         if (!cret) return -2;
1747         if (msgnum < 0) return -2;
1748
1749         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1750         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1751 }
1752
1753
1754 /* STLS */
1755 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1756 {
1757         return CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
1758 }
1759
1760
1761 /* QDIR */
1762 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
1763 {
1764         char *aaa;
1765
1766         if (!address) return -2;
1767         if (!cret) return -2;
1768
1769         aaa = (char *)malloc(strlen(address) + 6);
1770         if (!aaa) return -1;
1771
1772         sprintf(aaa, "QDIR %s", address);
1773         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1774 }
1775
1776
1777 /* IPGM */
1778 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
1779 {
1780         char aaa[30];
1781
1782         if (!cret) return -2;
1783         sprintf(aaa, "IPGM %d", secret);
1784         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1785 }
1786
1787
1788 /*
1789  * Not implemented:
1790  * 
1791  * CHAT
1792  * ETLS
1793  * EXPI
1794  * GTLS
1795  * IGAB
1796  * MSG3
1797  * MSG4
1798  * NDOP
1799  * NETP
1800  * NUOP
1801  * SMTP
1802  */
1803
1804
1805 /* ************************************************************************** */
1806 /*             Stuff below this line is not for public consumption            */
1807 /* ************************************************************************** */
1808
1809
1810 inline void CtdlIPC_lock(CtdlIPC *ipc)
1811 {
1812 #ifdef THREADED_CLIENT
1813         pthread_mutex_lock(&(ipc->mutex));
1814 #endif
1815 }
1816
1817
1818 inline void CtdlIPC_unlock(CtdlIPC *ipc)
1819 {
1820 #ifdef THREADED_CLIENT
1821         pthread_mutex_unlock(&(ipc->mutex));
1822 #endif
1823 }
1824
1825
1826 /* Read a listing from the server up to 000.  Append to dest if it exists */
1827 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
1828 {
1829         size_t length = 0;
1830         size_t linelength;
1831         char *ret;
1832         char aaa[SIZ];
1833
1834         ret = dest;
1835         if (ret != NULL) {
1836                 length = strlen(ret);
1837         }
1838         else {
1839                 ret = strdup("");
1840                 length = 0;
1841         }
1842
1843         while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
1844                 linelength = strlen(aaa);
1845                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
1846                 if (ret) {
1847                         strcpy(&ret[length], aaa);
1848                         length += linelength;
1849                         strcpy(&ret[length++], "\n");
1850                 }
1851         }
1852
1853         return(ret);
1854 }
1855
1856
1857 /* Send a listing to the server; generate the ending 000. */
1858 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
1859 {
1860         char *text;
1861
1862         text = (char *)malloc(strlen(listing) + 6);
1863         if (text) {
1864                 strcpy(text, listing);
1865                 while (text[strlen(text) - 1] == '\n')
1866                         text[strlen(text) - 1] = '\0';
1867                 strcat(text, "\n000");
1868                 CtdlIPC_putline(ipc, text);
1869                 free(text);
1870                 text = NULL;
1871         } else {
1872                 /* Malloc failed but we are committed to send */
1873                 /* This may result in extra blanks at the bottom */
1874                 CtdlIPC_putline(ipc, text);
1875                 CtdlIPC_putline(ipc, "000");
1876         }
1877         return 0;
1878 }
1879
1880
1881 /* Partial read of file from server */
1882 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
1883 {
1884         register size_t len = 0;
1885         char aaa[SIZ];
1886
1887         if (!buf) return -1;
1888         if (!cret) return -1;
1889         if (bytes < 1) return -1;
1890         if (offset < 0) return -1;
1891
1892         CtdlIPC_lock(ipc);
1893         sprintf(aaa, "READ %d|%d", offset, bytes);
1894         CtdlIPC_putline(ipc, aaa);
1895         CtdlIPC_getline(ipc, aaa);
1896         if (aaa[0] != '6')
1897                 strcpy(cret, &aaa[4]);
1898         else {
1899                 len = extract_long(&aaa[4], 0);
1900                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
1901                 if (*buf) {
1902                         /* I know what I'm doing */
1903                         serv_read(ipc, (*buf + offset), len);
1904                 } else {
1905                         /* We have to read regardless */
1906                         serv_read(ipc, aaa, len);
1907                         len = -1;
1908                 }
1909         }
1910         CtdlIPC_unlock(ipc);
1911         return len;
1912 }
1913
1914
1915 /* CLOS */
1916 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
1917 {
1918         register int ret;
1919
1920         if (!cret) return -2;
1921         if (!ipc->downloading) return -2;
1922
1923         ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
1924         if (ret / 100 == 2)
1925                 ipc->downloading = 0;
1926         return ret;
1927 }
1928
1929
1930 /* MSGP */
1931 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
1932         register int ret;
1933         char cmd[SIZ];
1934         
1935         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
1936         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1937         return ret;
1938 }
1939
1940
1941
1942 /* READ */
1943 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
1944                void (*progress_gauge_callback)(long, long), char *cret)
1945 {
1946         register size_t len;
1947
1948         if (!cret) return -1;
1949         if (!buf) return -1;
1950         if (*buf) return -1;
1951         if (!ipc->downloading) return -1;
1952
1953         len = 0;
1954         if (progress_gauge_callback)
1955                 progress_gauge_callback(len, bytes);
1956         while (len < bytes) {
1957                 register size_t block;
1958
1959                 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
1960                 if (block == -1) {
1961                         free(*buf);
1962                         return 0;
1963                 }
1964                 len += block;
1965                 if (progress_gauge_callback)
1966                         progress_gauge_callback(len, bytes);
1967         }
1968         return len;
1969 }
1970
1971
1972 /* READ - pipelined */
1973 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
1974                void (*progress_gauge_callback)(long, long), char *cret)
1975 {
1976         register size_t len;
1977         register int calls;     /* How many calls in the pipeline */
1978         register int i;         /* iterator */
1979         char aaa[4096];
1980
1981         if (!cret) return -1;
1982         if (!buf) return -1;
1983         if (*buf) return -1;
1984         if (!ipc->downloading) return -1;
1985
1986         *buf = (void *)realloc(*buf, bytes);
1987         if (!*buf) return -1;
1988
1989         len = 0;
1990         CtdlIPC_lock(ipc);
1991         if (progress_gauge_callback)
1992                 progress_gauge_callback(len, bytes);
1993
1994         /* How many calls will be in the pipeline? */
1995         calls = bytes / 4096;
1996         if (bytes % 4096) calls++;
1997
1998         /* Send all requests at once */
1999         for (i = 0; i < calls; i++) {
2000                 sprintf(aaa, "READ %d|4096", i * 4096);
2001                 CtdlIPC_putline(ipc, aaa);
2002         }
2003
2004         /* Receive all responses at once */
2005         for (i = 0; i < calls; i++) {
2006                 CtdlIPC_getline(ipc, aaa);
2007                 if (aaa[0] != '6')
2008                         strcpy(cret, &aaa[4]);
2009                 else {
2010                         len = extract_long(&aaa[4], 0);
2011                         /* I know what I'm doing */
2012                         serv_read(ipc, ((*buf) + (i * 4096)), len);
2013                 }
2014                 if (progress_gauge_callback)
2015                         progress_gauge_callback(i * 4096 + len, bytes);
2016         }
2017         CtdlIPC_unlock(ipc);
2018         return len;
2019 }
2020
2021
2022 /* UCLS */
2023 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2024 {
2025         register int ret;
2026         char cmd[8];
2027
2028         if (!cret) return -1;
2029         if (!ipc->uploading) return -1;
2030
2031         sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2032         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2033         ipc->uploading = 0;
2034         return ret;
2035 }
2036
2037
2038 /* WRIT */
2039 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2040                 void (*progress_gauge_callback)(long, long), char *cret)
2041 {
2042         register int ret = -1;
2043         register size_t offset = 0;
2044         size_t bytes;
2045         char aaa[SIZ];
2046         char buf[4096];
2047         FILE *fd;
2048
2049         if (!cret) return -1;
2050         if (!path) return -1;
2051         if (!*path) return -1;
2052
2053         fd = fopen(path, "r");
2054         if (!fd) return -2;
2055
2056         fseek(fd, 0L, SEEK_END);
2057         bytes = ftell(fd);
2058         rewind(fd);
2059
2060         if (progress_gauge_callback)
2061                 progress_gauge_callback(0, bytes);
2062
2063         while (offset < bytes) {
2064                 register size_t to_write;
2065
2066                 /* Read some data in */
2067                 to_write = fread(buf, 1, 4096, fd);
2068                 if (!to_write) {
2069                         if (feof(fd) || ferror(fd)) break;
2070                 }
2071                 sprintf(aaa, "WRIT %d", to_write);
2072                 CtdlIPC_putline(ipc, aaa);
2073                 CtdlIPC_getline(ipc, aaa);
2074                 strcpy(cret, &aaa[4]);
2075                 ret = atoi(aaa);
2076                 if (aaa[0] == '7') {
2077                         to_write = extract_long(&aaa[4], 0);
2078                         
2079                         serv_write(ipc, buf, to_write);
2080                         offset += to_write;
2081                         if (progress_gauge_callback)
2082                                 progress_gauge_callback(offset, bytes);
2083                         /* Detect short reads and back up if needed */
2084                         fseek(fd, offset, SEEK_SET);
2085                 } else {
2086                         break;
2087                 }
2088         }
2089         if (progress_gauge_callback)
2090                 progress_gauge_callback(1, 1);
2091         return (!ferror(fd) ? ret : -2);
2092 }
2093
2094
2095 /*
2096  * Generic command method.  This method should handle any server command
2097  * except for CHAT.  It takes the following arguments:
2098  *
2099  * ipc                  The server to speak with
2100  * command              Preformatted command to send to server
2101  * to_send              A text or binary file to send to server
2102  *                      (only sent if server requests it)
2103  * bytes_to_send        The number of bytes in to_send (required if
2104  *                      sending binary, optional if sending listing)
2105  * to_receive           Pointer to a NULL pointer, if the server
2106  *                      sends text or binary we will allocate memory
2107  *                      for the file and stuff it here
2108  * bytes_to_receive     If a file is received, we will store its
2109  *                      byte count here
2110  * proto_response       The protocol response.  Caller must provide
2111  *                      this buffer and ensure that it is at least
2112  *                      128 bytes in length.
2113  *
2114  * This function returns a number equal to the protocol response number,
2115  * -1 if an internal error occurred, -2 if caller provided bad values,
2116  * or 0 - the protocol response number if bad values were found during
2117  * the protocol exchange.
2118  * It stores the protocol response string (minus the number) in 
2119  * protocol_response as described above.  Some commands send additional
2120  * data in this string.
2121  */
2122 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2123                 const char *command, const char *to_send,
2124                 size_t bytes_to_send, char **to_receive, 
2125                 size_t *bytes_to_receive, char *proto_response)
2126 {
2127         char buf[SIZ];
2128         register int ret;
2129         int watch_ssl = 0;
2130
2131         if (!command) return -2;
2132         if (!proto_response) return -2;
2133
2134         if (ipc->ssl) watch_ssl = 1;
2135
2136         CtdlIPC_lock(ipc);
2137         CtdlIPC_putline(ipc, command);
2138         while (1) {
2139                 CtdlIPC_getline(ipc, proto_response);
2140                 if (proto_response[3] == '*')
2141                         express_msgs = 1;
2142                 ret = atoi(proto_response);
2143                 strcpy(proto_response, &proto_response[4]);
2144                 switch (ret / 100) {
2145                 default:                        /* Unknown, punt */
2146                 case 2:                         /* OK */
2147                 case 3:                         /* MORE_DATA */
2148                 case 5:                         /* ERROR */
2149                         /* Don't need to do anything */
2150                         break;
2151                 case 1:                         /* LISTING_FOLLOWS */
2152                         if (to_receive && !*to_receive && bytes_to_receive) {
2153                                 *to_receive = CtdlIPCReadListing(ipc, NULL);
2154                         } else { /* Drain */
2155                                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2156                                 ret = -ret;
2157                         }
2158                         break;
2159                 case 4:                         /* SEND_LISTING */
2160                         if (to_send) {
2161                                 CtdlIPCSendListing(ipc, to_send);
2162                         } else {
2163                                 /* No listing given, fake it */
2164                                 CtdlIPC_putline(ipc, "000");
2165                                 ret = -ret;
2166                         }
2167                         break;
2168                 case 6:                         /* BINARY_FOLLOWS */
2169                         if (to_receive && !*to_receive && bytes_to_receive) {
2170                                 *bytes_to_receive =
2171                                         extract_long(proto_response, 0);
2172                                 *to_receive = (char *)
2173                                         malloc((size_t)*bytes_to_receive);
2174                                 if (!*to_receive) {
2175                                         ret = -1;
2176                                 } else {
2177                                         serv_read(ipc, *to_receive,
2178                                                         *bytes_to_receive);
2179                                 }
2180                         } else {
2181                                 /* Drain */
2182                                 size_t drain;
2183
2184                                 drain = extract_long(proto_response, 0);
2185                                 while (drain > SIZ) {
2186                                         serv_read(ipc, buf, SIZ);
2187                                         drain -= SIZ;
2188                                 }
2189                                 serv_read(ipc, buf, drain);
2190                                 ret = -ret;
2191                         }
2192                         break;
2193                 case 7:                         /* SEND_BINARY */
2194                         if (to_send && bytes_to_send) {
2195                                 serv_write(ipc, to_send, bytes_to_send);
2196                         } else if (bytes_to_send) {
2197                                 /* Fake it, send nulls */
2198                                 size_t fake;
2199
2200                                 fake = bytes_to_send;
2201                                 memset(buf, '\0', SIZ);
2202                                 while (fake > SIZ) {
2203                                         serv_write(ipc, buf, SIZ);
2204                                         fake -= SIZ;
2205                                 }
2206                                 serv_write(ipc, buf, fake);
2207                                 ret = -ret;
2208                         } /* else who knows?  DANGER WILL ROBINSON */
2209                         break;
2210                 case 8:                         /* START_CHAT_MODE */
2211                         if (!strncasecmp(command, "CHAT", 4)) {
2212                                 /* Don't call chatmode with generic! */
2213                                 CtdlIPC_putline(ipc, "/quit");
2214                                 ret = -ret;
2215                         } else {
2216                                 /* In this mode we send then receive listing */
2217                                 if (to_send) {
2218                                         CtdlIPCSendListing(ipc, to_send);
2219                                 } else {
2220                                         /* No listing given, fake it */
2221                                         CtdlIPC_putline(ipc, "000");
2222                                         ret = -ret;
2223                                 }
2224                                 if (to_receive && !*to_receive
2225                                                 && bytes_to_receive) {
2226                                         *to_receive = CtdlIPCReadListing(ipc, NULL);
2227                                 } else { /* Drain */
2228                                         while (CtdlIPC_getline(ipc, buf),
2229                                                         strcmp(buf, "000")) ;
2230                                         ret = -ret;
2231                                 }
2232                         }
2233                         break;
2234                 case 9:                         /* ASYNC_MSG */
2235                         /* CtdlIPCDoAsync(ret, proto_response); */
2236                         free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
2237                         break;
2238                 }
2239                 if (ret / 100 != 9)
2240                         break;
2241         }
2242         CtdlIPC_unlock(ipc);
2243         return ret;
2244 }