* Build fixes for Solaris box without OpenSSL installed
[citadel.git] / citadel / citadel_ipc.c
1 /* $Id$ */
2
3 #define UDS                     "_UDS_"
4 #define DEFAULT_HOST            UDS
5 #define DEFAULT_PORT            "citadel"
6
7 #include "sysdep.h"
8 #if TIME_WITH_SYS_TIME
9 # include <sys/time.h>
10 # include <time.h>
11 #else
12 # if HAVE_SYS_TIME_H
13 #  include <sys/time.h>
14 # else
15 #  include <time.h>
16 # endif
17 #endif
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include <string.h>
22 #include <malloc.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <sys/socket.h>
26 #include <arpa/inet.h>
27 #include <netinet/in.h>
28 #include <netdb.h>
29 #include <sys/un.h>
30 #include <errno.h>
31 #ifdef THREADED_CLIENT
32 #include <pthread.h>
33 #endif
34 #include "citadel.h"
35 #include "citadel_ipc.h"
36 #include "citadel_decls.h"
37 #include "tools.h"
38
39 #ifdef THREADED_CLIENT
40 pthread_mutex_t rwlock;
41 #endif
42
43 #ifdef HAVE_OPENSSL
44 static SSL_CTX *ssl_ctx;
45 char arg_encrypt;
46 char rc_encrypt;
47 #ifdef THREADED_CLIENT
48 pthread_mutex_t **Critters;                     /* Things that need locking */
49 #endif /* THREADED_CLIENT */
50
51 #endif /* HAVE_OPENSSL */
52
53 #ifndef INADDR_NONE
54 #define INADDR_NONE 0xffffffff
55 #endif
56
57 static void (*status_hook)(char *s) = NULL;
58
59 void setCryptoStatusHook(void (*hook)(char *s)) {
60         status_hook = hook;
61 }
62
63
64 char express_msgs = 0;
65
66
67 static void serv_read(CtdlIPC *ipc, char *buf, int bytes);
68 static void serv_write(CtdlIPC *ipc, const char *buf, int nbytes);
69 #ifdef HAVE_OPENSSL
70 static void serv_read_ssl(CtdlIPC *ipc, char *buf, int bytes);
71 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, int nbytes);
72 static void ssl_lock(int mode, int n, const char *file, int line);
73 static void endtls(SSL *ssl);
74 #ifdef THREADED_CLIENT
75 static unsigned long id_callback(void);
76 #endif /* THREADED_CLIENT */
77 #endif /* HAVE_OPENSSL */
78
79
80 /*
81  * Does nothing.  The server should always return 200.
82  */
83 int CtdlIPCNoop(CtdlIPC *ipc)
84 {
85         char aaa[128];
86
87         return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
88 }
89
90
91 /*
92  * Does nothing interesting.  The server should always return 200
93  * along with your string.
94  */
95 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
96 {
97         register int ret;
98         char *aaa;
99         
100         if (!arg) return -2;
101         if (!cret) return -2;
102
103         aaa = (char *)malloc((size_t)(strlen(arg) + 6));
104         if (!aaa) return -1;
105
106         sprintf(aaa, "ECHO %s", arg);
107         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
108         free(aaa);
109         return ret;
110 }
111
112
113 /*
114  * Asks the server to close the connecction.
115  * Should always return 200.
116  */
117 int CtdlIPCQuit(CtdlIPC *ipc)
118 {
119         register int ret;
120         char aaa[128];
121
122         CtdlIPC_lock(ipc);
123         CtdlIPC_putline(ipc, "QUIT");
124         CtdlIPC_getline(ipc, aaa);
125         ret = atoi(aaa);
126         CtdlIPC_unlock(ipc);
127         return ret;
128 }
129
130
131 /*
132  * Asks the server to logout.  Should always return 200, even if no user
133  * was logged in.  The user will not be logged in after this!
134  */
135 int CtdlIPCLogout(CtdlIPC *ipc)
136 {
137         register int ret;
138         char aaa[128];
139
140         CtdlIPC_lock(ipc);
141         CtdlIPC_putline(ipc, "LOUT");
142         CtdlIPC_getline(ipc, aaa);
143         ret = atoi(aaa);
144         CtdlIPC_unlock(ipc);
145         return ret;
146 }
147
148
149 /*
150  * First stage of authentication - pass the username.  Returns 300 if the
151  * username is able to log in, with the username correctly spelled in cret.
152  * Returns various 500 error codes if the user doesn't exist, etc.
153  */
154 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
155 {
156         register int ret;
157         char *aaa;
158
159         if (!username) return -2;
160         if (!cret) return -2;
161
162         aaa = (char *)malloc((size_t)(strlen(username) + 6));
163         if (!aaa) return -1;
164
165         sprintf(aaa, "USER %s", username);
166         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
167         free(aaa);
168         return ret;
169 }
170
171
172 /*
173  * Second stage of authentication - provide password.  The server returns
174  * 200 and several arguments in cret relating to the user's account.
175  */
176 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
177 {
178         register int ret;
179         char *aaa;
180
181         if (!passwd) return -2;
182         if (!cret) return -2;
183
184         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
185         if (!aaa) return -1;
186
187         sprintf(aaa, "PASS %s", passwd);
188         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
189         free(aaa);
190         return ret;
191 }
192
193
194 /*
195  * Create a new user.  This returns 200 plus the same arguments as TryPassword
196  * if selfservice is nonzero, unless there was a problem creating the account.
197  * If selfservice is zero, creates a new user but does not log out the existing
198  * user - intended for use by system administrators to create accounts on
199  * behalf of other users.
200  */
201 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
202 {
203         register int ret;
204         char *aaa;
205
206         if (!username) return -2;
207         if (!cret) return -2;
208
209         aaa = (char *)malloc((size_t)(strlen(username) + 6));
210         if (!aaa) return -1;
211
212         sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
213         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
214         free(aaa);
215         return ret;
216 }
217
218
219 /*
220  * Changes the user's password.  Returns 200 if changed, errors otherwise.
221  */
222 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
223 {
224         register int ret;
225         char *aaa;
226
227         if (!passwd) return -2;
228         if (!cret) return -2;
229
230         aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
231         if (!aaa) return -1;
232
233         sprintf(aaa, "SETP %s", passwd);
234         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
235         free(aaa);
236         return ret;
237 }
238
239
240 /* LKRN */
241 /* Caller must free the march list */
242 /* which is 0 = LRMS, 1 = LKRN, 2 = LKRO, 3 = LKRA, 4 = LZRM */
243 /* floor is -1 for all, or floornum */
244 int CtdlIPCKnownRooms(CtdlIPC *ipc, int which, int floor, struct march **listing, char *cret)
245 {
246         register int ret;
247         struct march *march = NULL;
248         static char *proto[] = {"LRMS", "LKRN", "LKRO", "LKRA", "LZRM" };
249         char aaa[SIZ];
250         char *bbb = NULL;
251         size_t bbbsize;
252
253         if (!listing) return -2;
254         if (*listing) return -2;        /* Free the listing first */
255         if (!cret) return -2;
256         if (which < 0 || which > 4) return -2;
257         if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
258
259         sprintf(aaa, "%s %d", proto[which], floor);
260         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbbsize, cret);
261         if (ret / 100 == 1) {
262                 struct march *mptr;
263
264                 while (bbb && strlen(bbb)) {
265                         int a;
266
267                         extract_token(aaa, bbb, 0, '\n');
268                         a = strlen(aaa);
269                         memmove(bbb, bbb + a + 1, strlen(bbb) - a);
270                         mptr = (struct march *) malloc(sizeof (struct march));
271                         if (mptr) {
272                                 mptr->next = NULL;
273                                 extract(mptr->march_name, aaa, 0);
274                                 mptr->march_floor = (char) extract_int(aaa, 2);
275                                 mptr->march_order = (char) extract_int(aaa, 3);
276                                 if (march == NULL)
277                                         march = mptr;
278                                 else {
279                                         struct march *mptr2;
280
281                                         mptr2 = march;
282                                         while (mptr2->next != NULL)
283                                                 mptr2 = mptr2->next;
284                                         mptr2->next = mptr;
285                                 }
286                         }
287                 }
288         }
289         *listing = march;
290         return ret;
291 }
292
293
294 /* GETU */
295 /* Caller must free the struct usersupp; caller may pass an existing one */
296 int CtdlIPCGetConfig(CtdlIPC *ipc, struct usersupp **uret, char *cret)
297 {
298         register int ret;
299
300         if (!cret) return -2;
301         if (!uret) return -2;
302         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof (struct usersupp));
303         if (!*uret) return -1;
304
305         ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
306         if (ret / 100 == 2) {
307                 uret[0]->USscreenwidth = extract_int(cret, 0);
308                 uret[0]->USscreenheight = extract_int(cret, 1);
309                 uret[0]->flags = extract_int(cret, 2);
310         }
311         return ret;
312 }
313
314
315 /* SETU */
316 int CtdlIPCSetConfig(CtdlIPC *ipc, struct usersupp *uret, char *cret)
317 {
318         char aaa[48];
319
320         if (!uret) return -2;
321         if (!cret) return -2;
322
323         sprintf(aaa, "SETU %d|%d|%d",
324                         uret->USscreenwidth, uret->USscreenheight,
325                         uret->flags);
326         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
327 }
328
329
330 /* GOTO */
331 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
332                 struct ctdlipcroom **rret, char *cret)
333 {
334         register int ret;
335         char *aaa;
336
337         if (!cret) return -2;
338         if (!rret) return -2;
339         if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
340         if (!*rret) return -1;
341
342         if (passwd) {
343                 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
344                 if (!aaa) {
345                         free(*rret);
346                         return -1;
347                 }
348                 sprintf(aaa, "GOTO %s|%s", room, passwd);
349         } else {
350                 aaa = (char *)malloc(strlen(room) + 6);
351                 if (!aaa) {
352                         free(*rret);
353                         return -1;
354                 }
355                 sprintf(aaa, "GOTO %s", room);
356         }
357         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
358         if (ret / 100 == 2) {
359                 extract(rret[0]->RRname, cret, 0);
360                 rret[0]->RRunread = extract_long(cret, 1);
361                 rret[0]->RRtotal = extract_long(cret, 2);
362                 rret[0]->RRinfoupdated = extract_int(cret, 3);
363                 rret[0]->RRflags = extract_int(cret, 4);
364                 rret[0]->RRhighest = extract_long(cret, 5);
365                 rret[0]->RRlastread = extract_long(cret, 6);
366                 rret[0]->RRismailbox = extract_int(cret, 7);
367                 rret[0]->RRaide = extract_int(cret, 8);
368                 rret[0]->RRnewmail = extract_long(cret, 9);
369                 rret[0]->RRfloor = extract_int(cret, 10);
370         } else {
371                 free(*rret);
372         }
373         return ret;
374 }
375
376
377 /* MSGS */
378 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
379 /* whicharg is number of messages, applies to last, first, gt, lt */
380 int CtdlIPCGetMessages(CtdlIPC *ipc, int which, int whicharg, const char *template,
381                 long **mret, char *cret)
382 {
383         register int ret;
384         register long count = 0;
385         static char *proto[] =
386                 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
387         char aaa[33];
388         char *bbb;
389         size_t bbbsize;
390
391         if (!cret) return -2;
392         if (!mret) return -2;
393         if (*mret) return -2;
394         if (which < 0 || which > 6) return -2;
395
396         if (which <= 2)
397                 sprintf(aaa, "MSGS %s||%d", proto[which],
398                                 (template) ? 1 : 0);
399         else
400                 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
401                                 (template) ? 1 : 0);
402         if (template) count = strlen(template);
403         ret = CtdlIPCGenericCommand(ipc, aaa, template, count, &bbb, &bbbsize, cret);
404         count = 0;
405         while (strlen(bbb)) {
406                 int a;
407
408                 extract_token(aaa, bbb, 0, '\n');
409                 a = strlen(aaa);
410                 memmove(aaa, bbb + a + 1, strlen(bbb) - a - 1);
411                 *mret = (long *)realloc(mret,
412                                         (size_t)((count + 1) * sizeof (long)));
413                 if (*mret)
414                         *mret[count++] = atol(aaa);
415                 *mret[count] = 0L;
416         }
417         return ret;
418 }
419
420
421 /* MSG0, MSG2 */
422 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
423                 struct ctdlipcmessage **mret, char *cret)
424 {
425         register int ret;
426         char aaa[SIZ];
427         char *bbb = NULL;
428         size_t bbbsize;
429         int multipart_hunting = 0;
430         char multipart_prefix[SIZ];
431
432         if (!cret) return -1;
433         if (!mret) return -1;
434         if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
435         if (!*mret) return -1;
436         if (!msgnum) return -1;
437
438         strcpy(mret[0]->content_type, "");
439         sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
440         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbbsize, cret);
441         if (ret / 100 == 1) {
442                 if (as_mime != 2) {
443                         strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
444                         while (strlen(bbb) > 4 && bbb[4] == '=') {
445                                 extract_token(aaa, bbb, 0, '\n');
446                                 remove_token(bbb, 0, '\n');
447
448                                 if (!strncasecmp(aaa, "nhdr=yes", 8))
449                                         mret[0]->nhdr = 1;
450                                 else if (!strncasecmp(aaa, "from=", 5))
451                                         strcpy(mret[0]->author, &aaa[5]);
452                                 else if (!strncasecmp(aaa, "type=", 5))
453                                         mret[0]->type = atoi(&aaa[5]);
454                                 else if (!strncasecmp(aaa, "msgn=", 5))
455                                         strcpy(mret[0]->msgid, &aaa[5]);
456                                 else if (!strncasecmp(aaa, "subj=", 5))
457                                         strcpy(mret[0]->subject, &aaa[5]);
458                                 else if (!strncasecmp(aaa, "rfca=", 5))
459                                         strcpy(mret[0]->email, &aaa[5]);
460                                 else if (!strncasecmp(aaa, "hnod=", 5))
461                                         strcpy(mret[0]->hnod, &aaa[5]);
462                                 else if (!strncasecmp(aaa, "room=", 5))
463                                         strcpy(mret[0]->room, &aaa[5]);
464                                 else if (!strncasecmp(aaa, "node=", 5))
465                                         strcpy(mret[0]->node, &aaa[5]);
466                                 else if (!strncasecmp(aaa, "rcpt=", 5))
467                                         strcpy(mret[0]->recipient, &aaa[5]);
468                                 else if (!strncasecmp(aaa, "time=", 5))
469                                         mret[0]->time = atol(&aaa[5]);
470
471                                 /* Multipart/alternative prefix & suffix strings help
472                                  * us to determine which part we want to download.
473                                  */
474                                 else if (!strncasecmp(aaa, "pref=", 5)) {
475                                         extract(multipart_prefix, &aaa[5], 1);
476                                         if (!strcasecmp(multipart_prefix,
477                                            "multipart/alternative")) {
478                                                 ++multipart_hunting;
479                                         }
480                                 }
481                                 else if (!strncasecmp(aaa, "suff=", 5)) {
482                                         extract(multipart_prefix, &aaa[5], 1);
483                                         if (!strcasecmp(multipart_prefix,
484                                            "multipart/alternative")) {
485                                                 ++multipart_hunting;
486                                         }
487                                 }
488
489                                 else if (!strncasecmp(aaa, "part=", 5)) {
490                                         struct parts *ptr, *chain;
491         
492                                         ptr = (struct parts *)calloc(1, sizeof (struct parts));
493                                         if (ptr) {
494
495                                                 /* Fill the buffers for the caller */
496                                                 extract(ptr->name, &aaa[5], 0);
497                                                 extract(ptr->filename, &aaa[5], 1);
498                                                 extract(ptr->number, &aaa[5], 2);
499                                                 extract(ptr->disposition, &aaa[5], 3);
500                                                 extract(ptr->mimetype, &aaa[5], 4);
501                                                 ptr->length = extract_long(&aaa[5], 5);
502                                                 if (!mret[0]->attachments)
503                                                         mret[0]->attachments = ptr;
504                                                 else {
505                                                         chain = mret[0]->attachments;
506                                                         while (chain->next)
507                                                                 chain = chain->next;
508                                                         chain->next = ptr;
509                                                 }
510
511                                                 /* Now handle multipart/alternative */
512                                                 if (multipart_hunting > 0) {
513                                                         if ( (!strcasecmp(ptr->mimetype,
514                                                              "text/plain"))
515                                                            || (!strcasecmp(ptr->mimetype,
516                                                               "text/html")) ) {
517                                                                 strcpy(mret[0]->mime_chosen,
518                                                                         ptr->number);
519                                                         }
520                                                 }
521
522                                         }
523                                 }
524                         }
525                         /* Eliminate "text\n" */
526                         remove_token(bbb, 0, '\n');
527
528                         /* If doing a MIME thing, pull out the extra headers */
529                         if (as_mime == 4) {
530                                 do {
531                                         if (!strncasecmp(bbb, "Content-type: ", 14)) {
532                                                 extract_token(mret[0]->content_type, bbb, 0, '\n');
533                                                 strcpy(mret[0]->content_type,
534                                                         &mret[0]->content_type[14]);
535                                                 striplt(mret[0]->content_type);
536                                         }
537                                         remove_token(bbb, 0, '\n');
538                                 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
539                         }
540
541
542                 }
543                 if (strlen(bbb)) {
544                         /* Strip trailing whitespace */
545                         bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
546                 } else {
547                         bbb = (char *)realloc(bbb, 1);
548                         *bbb = '\0';
549                 }
550                 mret[0]->text = bbb;
551         }
552         return ret;
553 }
554
555
556 /* WHOK */
557 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
558 {
559         register int ret;
560         size_t bytes;
561
562         if (!cret) return -2;
563         if (!listing) return -2;
564         if (*listing) return -2;
565
566         ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
567         return ret;
568 }
569
570
571 /* INFO */
572 int CtdlIPCServerInfo(CtdlIPC *ipc, struct CtdlServInfo *ServInfo, char *cret)
573 {
574         register int ret;
575         size_t bytes;
576         char *listing = NULL;
577         char buf[SIZ];
578
579         if (!cret) return -2;
580         if (!ServInfo) return -2;
581
582         ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
583         if (ret / 100 == 1) {
584                 int line = 0;
585
586                 while (*listing && strlen(listing)) {
587                         extract_token(buf, listing, 0, '\n');
588                         remove_token(listing, 0, '\n');
589                         switch (line++) {
590                         case 0:         ServInfo->serv_pid = atoi(buf);
591                                         break;
592                         case 1:         strcpy(ServInfo->serv_nodename,buf);
593                                         break;
594                         case 2:         strcpy(ServInfo->serv_humannode,buf);
595                                         break;
596                         case 3:         strcpy(ServInfo->serv_fqdn,buf);
597                                         break;
598                         case 4:         strcpy(ServInfo->serv_software,buf);
599                                         break;
600                         case 5:         ServInfo->serv_rev_level = atoi(buf);
601                                         break;
602                         case 6:         strcpy(ServInfo->serv_bbs_city,buf);
603                                         break;
604                         case 7:         strcpy(ServInfo->serv_sysadm,buf);
605                                         break;
606                         case 9:         strcpy(ServInfo->serv_moreprompt,buf);
607                                         break;
608                         case 10:        ServInfo->serv_ok_floors = atoi(buf);
609                                         break;
610                         case 11:        ServInfo->serv_paging_level = atoi(buf);
611                                         break;
612                         case 13:        ServInfo->serv_supports_qnop = atoi(buf);
613                                         break;
614                         }
615                 }
616
617         }
618         return ret;
619 }
620
621
622 /* RDIR */
623 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
624 {
625         register int ret;
626         size_t bytes;
627
628         if (!cret) return -2;
629         if (!listing) return -2;
630         if (*listing) return -2;
631
632         ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
633         return ret;
634 }
635
636
637 /*
638  * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
639  */
640 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
641 {
642         register int ret;
643         char aaa[16];
644
645         if (!cret) return -2;
646
647         if (msgnum)
648                 sprintf(aaa, "SLRP %ld", msgnum);
649         else
650                 sprintf(aaa, "SLRP HIGHEST");
651         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
652         return ret;
653 }
654
655
656 /* INVT */
657 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
658 {
659         register int ret;
660         char *aaa;
661
662         if (!cret) return -2;
663         if (!username) return -2;
664
665         aaa = (char *)malloc(strlen(username) + 6);
666         if (!aaa) return -1;
667
668         sprintf(aaa, "INVT %s", username);
669         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
670         free(aaa);
671         return ret;
672 }
673
674
675 /* KICK */
676 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
677 {
678         register int ret;
679         char *aaa;
680
681         if (!cret) return -1;
682         if (!username) return -1;
683
684         aaa = (char *)malloc(strlen(username) + 6);
685
686         sprintf(aaa, "KICK %s", username);
687         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
688         free(aaa);
689         return ret;
690 }
691
692
693 /* GETR */
694 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct quickroom **qret, char *cret)
695 {
696         register int ret;
697
698         if (!cret) return -2;
699         if (!qret) return -2;
700         if (!*qret) *qret = (struct quickroom *)calloc(1, sizeof (struct quickroom));
701         if (!*qret) return -1;
702
703         ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
704         if (ret / 100 == 2) {
705                 extract(qret[0]->QRname, cret, 0);
706                 extract(qret[0]->QRpasswd, cret, 1);
707                 extract(qret[0]->QRdirname, cret, 2);
708                 qret[0]->QRflags = extract_int(cret, 3);
709                 qret[0]->QRfloor = extract_int(cret, 4);
710                 qret[0]->QRorder = extract_int(cret, 5);
711         }
712         return ret;
713 }
714
715
716 /* SETR */
717 /* set forget to kick all users out of room */
718 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct quickroom *qret, char *cret)
719 {
720         register int ret;
721         char *aaa;
722
723         if (!cret) return -2;
724         if (!qret) return -2;
725
726         aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
727                         strlen(qret->QRdirname) + 52);
728         if (!aaa) return -1;
729
730         sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d",
731                         qret->QRname, qret->QRpasswd, qret->QRdirname,
732                         qret->QRflags, forget, qret->QRfloor, qret->QRorder);
733         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
734         free(aaa);
735         return ret;
736 }
737
738
739 /* GETA */
740 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
741 {
742         if (!cret) return -1;
743
744         return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
745 }
746
747
748 /* SETA */
749 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
750 {
751         register int ret;
752         char *aaa;
753
754         if (!cret) return -2;
755         if (!username) return -2;
756
757         aaa = (char *)malloc(strlen(username) + 6);
758         if (!aaa) return -1;
759
760         sprintf(aaa, "SETA %s", username);
761         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
762         free(aaa);
763         return ret;
764 }
765
766
767 /* ENT0 */
768 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, const struct ctdlipcmessage *mr, char *cret)
769 {
770         register int ret;
771         char *aaa;
772
773         if (!cret) return -2;
774         if (!mr) return -2;
775
776         aaa = (char *)malloc(strlen(mr->recipient) + strlen(mr->author) + 40);
777         if (!aaa) return -1;
778
779         sprintf(aaa, "ENT0 %d|%s|%d|%d|%s", flag, mr->recipient, mr->anonymous,
780                         mr->type, mr->author);
781         ret = CtdlIPCGenericCommand(ipc, aaa, mr->text, strlen(mr->text), NULL,
782                         NULL, cret);
783         free(aaa);
784         return ret;
785 }
786
787
788 /* RINF */
789 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
790 {
791         size_t bytes;
792
793         if (!cret) return -2;
794         if (!iret) return -2;
795         if (*iret) return -2;
796
797         return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
798 }
799
800
801 /* DELE */
802 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
803 {
804         char aaa[16];
805
806         if (!cret) return -2;
807         if (!msgnum) return -2;
808
809         sprintf(aaa, "DELE %ld", msgnum);
810         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
811 }
812
813
814 /* MOVE */
815 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
816 {
817         register int ret;
818         char *aaa;
819
820         if (!cret) return -2;
821         if (!destroom) return -2;
822         if (!msgnum) return -2;
823
824         aaa = (char *)malloc(strlen(destroom) + 28);
825         if (!aaa) return -1;
826
827         sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
828         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
829         free(aaa);
830         return ret;
831 }
832
833
834 /* KILL */
835 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
836 {
837         char aaa[16];
838
839         if (!cret) return -2;
840
841         sprintf(aaa, "KILL %d", for_real);
842         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
843 }
844
845
846 /* CRE8 */
847 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
848                 const char *password, int floor, char *cret)
849 {
850         register int ret;
851         char *aaa;
852
853         if (!cret) return -2;
854         if (!roomname) return -2;
855
856         if (password) {
857                 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
858                 if (!aaa) return -1;
859                 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
860                                 password, floor);
861         } else {
862                 aaa = (char *)malloc(strlen(roomname) + 40);
863                 if (!aaa) return -1;
864                 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
865                                 floor);
866         }
867         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
868         free(aaa);
869         return ret;
870 }
871
872
873 /* FORG */
874 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
875 {
876         if (!cret) return -2;
877
878         return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
879 }
880
881
882 /* MESG */
883 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
884 {
885         register int ret;
886         char *aaa;
887         size_t bytes;
888
889         if (!cret) return -2;
890         if (!mret) return -2;
891         if (*mret) return -2;
892         if (!message) return -2;
893
894         aaa = (char *)malloc(strlen(message) + 6);
895         if (!aaa) return -1;
896
897         sprintf(aaa, "MESG %s", message);
898         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
899         free(aaa);
900         return ret;
901 }
902
903
904 /* GNUR */
905 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
906 {
907         if (!cret) return -2;
908
909         return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
910 }
911
912
913 /* GREG */
914 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
915 {
916         register int ret;
917         char *aaa;
918         size_t bytes;
919
920         if (!cret) return -2;
921         if (!rret) return -2;
922         if (*rret) return -2;
923
924         if (username)
925                 aaa = (char *)malloc(strlen(username) + 6);
926         else
927                 aaa = (char *)malloc(12);
928         if (!aaa) return -1;
929
930         if (username)
931                 sprintf(aaa, "GREG %s", username);
932         else
933                 sprintf(aaa, "GREG _SELF_");
934         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
935         free(aaa);
936         return ret;
937 }
938
939
940 /* VALI */
941 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
942 {
943         register int ret;
944         char *aaa;
945
946         if (!cret) return -2;
947         if (!username) return -2;
948         if (axlevel < 0 || axlevel > 7) return -2;
949
950         aaa = (char *)malloc(strlen(username) + 17);
951         if (!aaa) return -1;
952
953         sprintf(aaa, "VALI %s|%d", username, axlevel);
954         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
955         free(aaa);
956         return ret;
957 }
958
959
960 /* EINF */
961 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
962 {
963         char aaa[16];
964
965         if (!cret) return -1;
966         if (!info) return -1;
967
968         sprintf(aaa, "EINF %d", for_real);
969         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
970 }
971
972
973 /* LIST */
974 int CtdlIPCUserListing(CtdlIPC *ipc, char **listing, char *cret)
975 {
976         size_t bytes;
977
978         if (!cret) return -1;
979         if (!listing) return -1;
980         if (*listing) return -1;
981
982         return CtdlIPCGenericCommand(ipc, "LIST", NULL, 0, listing, &bytes, cret);
983 }
984
985
986 /* REGI */
987 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
988 {
989         if (!cret) return -1;
990         if (!info) return -1;
991
992         return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
993                         NULL, NULL, cret);
994 }
995
996
997 /* CHEK */
998 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
999 {
1000         register int ret;
1001
1002         if (!cret) return -1;
1003         if (!chek) return -1;
1004
1005         ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1006         if (ret / 100 == 2) {
1007                 chek->newmail = extract_long(cret, 0);
1008                 chek->needregis = extract_int(cret, 1);
1009                 chek->needvalid = extract_int(cret, 2);
1010         }
1011         return ret;
1012 }
1013
1014
1015 /* DELF */
1016 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1017 {
1018         register int ret;
1019         char *aaa;
1020
1021         if (!cret) return -2;
1022         if (!filename) return -2;
1023         
1024         aaa = (char *)malloc(strlen(filename) + 6);
1025         if (!aaa) return -1;
1026
1027         sprintf(aaa, "DELF %s", filename);
1028         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1029         free(aaa);
1030         return ret;
1031 }
1032
1033
1034 /* MOVF */
1035 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1036 {
1037         register int ret;
1038         char *aaa;
1039
1040         if (!cret) return -2;
1041         if (!filename) return -2;
1042         if (!destroom) return -2;
1043
1044         aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1045         if (!aaa) return -1;
1046
1047         sprintf(aaa, "MOVF %s|%s", filename, destroom);
1048         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1049         free(aaa);
1050         return ret;
1051 }
1052
1053
1054 /* NETF */
1055 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1056 {
1057         register int ret;
1058         char *aaa;
1059
1060         if (!cret) return -2;
1061         if (!filename) return -2;
1062         if (!destnode) return -2;
1063
1064         aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1065         if (!aaa) return -1;
1066
1067         sprintf(aaa, "NETF %s|%s", filename, destnode);
1068         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1069         free(aaa);
1070         return ret;
1071 }
1072
1073
1074 /* RWHO */
1075 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1076 {
1077         register int ret;
1078         size_t bytes;
1079
1080         if (!cret) return -1;
1081         if (!listing) return -1;
1082         if (*listing) return -1;
1083
1084         *stamp = CtdlIPCServerTime(ipc, cret);
1085         if (!*stamp)
1086                 *stamp = time(NULL);
1087         ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1088         return ret;
1089 }
1090
1091
1092 /* OPEN */
1093 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1094                 void (*progress_gauge_callback)(long, long), char *cret)
1095 {
1096         register int ret;
1097         size_t bytes;
1098         time_t last_mod;
1099         char mimetype[SIZ];
1100         char *aaa;
1101
1102         if (!cret) return -2;
1103         if (!filename) return -2;
1104         if (!buf) return -2;
1105         if (*buf) return -2;
1106         if (ipc->downloading) return -2;
1107
1108         aaa = (char *)malloc(strlen(filename) + 6);
1109         if (!aaa) return -1;
1110
1111         sprintf(aaa, "OPEN %s", filename);
1112         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1113         free(aaa);
1114         if (ret / 100 == 2) {
1115                 ipc->downloading = 1;
1116                 bytes = extract_long(cret, 0);
1117                 last_mod = extract_int(cret, 1);
1118                 extract(mimetype, cret, 2);
1119                 ret = CtdlIPCReadDownload(ipc, buf, bytes, progress_gauge_callback, cret);
1120 /*              ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, progress_gauge_callback, cret); */
1121                 ret = CtdlIPCEndDownload(ipc, cret);
1122                 if (ret / 100 == 2)
1123                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1124                                         filename, mimetype);
1125         }
1126         return ret;
1127 }
1128
1129
1130 /* OPNA */
1131 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part, void **buf,
1132                 void (*progress_gauge_callback)(long, long), char *cret)
1133 {
1134         register int ret;
1135         size_t bytes;
1136         time_t last_mod;
1137         char filename[SIZ];
1138         char mimetype[SIZ];
1139         char *aaa;
1140
1141         if (!cret) return -2;
1142         if (!buf) return -2;
1143         if (*buf) return -2;
1144         if (!part) return -2;
1145         if (!msgnum) return -2;
1146         if (ipc->downloading) return -2;
1147
1148         aaa = (char *)malloc(strlen(part) + 17);
1149         if (!aaa) return -1;
1150
1151         sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1152         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1153         free(aaa);
1154         if (ret / 100 == 2) {
1155                 ipc->downloading = 1;
1156                 bytes = extract_long(cret, 0);
1157                 last_mod = extract_int(cret, 1);
1158                 extract(mimetype, cret, 2);
1159                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, progress_gauge_callback, cret);
1160                 ret = CtdlIPCEndDownload(ipc, cret);
1161                 if (ret / 100 == 2)
1162                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1163                                         filename, mimetype);
1164         }
1165         return ret;
1166 }
1167
1168
1169 /* OIMG */
1170 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1171                 void (*progress_gauge_callback)(long, long), char *cret)
1172 {
1173         register int ret;
1174         size_t bytes;
1175         time_t last_mod;
1176         char mimetype[SIZ];
1177         char *aaa;
1178
1179         if (!cret) return -1;
1180         if (!buf) return -1;
1181         if (*buf) return -1;
1182         if (!filename) return -1;
1183         if (ipc->downloading) return -1;
1184
1185         aaa = (char *)malloc(strlen(filename) + 6);
1186         if (!aaa) return -1;
1187
1188         sprintf(aaa, "OIMG %s", filename);
1189         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1190         free(aaa);
1191         if (ret / 100 == 2) {
1192                 ipc->downloading = 1;
1193                 bytes = extract_long(cret, 0);
1194                 last_mod = extract_int(cret, 1);
1195                 extract(mimetype, cret, 2);
1196                 ret = CtdlIPCReadDownload(ipc, buf, bytes, progress_gauge_callback, cret);
1197                 ret = CtdlIPCEndDownload(ipc, cret);
1198                 if (ret / 100 == 2)
1199                         sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
1200                                         filename, mimetype);
1201         }
1202         return ret;
1203 }
1204
1205
1206 /* UOPN */
1207 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1208                 const char *path, void (*progress_gauge_callback)(long, long),
1209                 char *cret)
1210 {
1211         register int ret;
1212         char *aaa;
1213
1214         if (!cret) return -1;
1215         if (!save_as) return -1;
1216         if (!comment) return -1;
1217         if (!path) return -1;
1218         if (!*path) return -1;
1219         if (ipc->uploading) return -1;
1220
1221         aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1222         if (!aaa) return -1;
1223
1224         sprintf(aaa, "UOPN %s|%s", save_as, comment);
1225         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1226         free(aaa);
1227         if (ret / 100 == 2) {
1228                 ipc->uploading = 1;
1229                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1230                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1231                 ipc->uploading = 0;
1232         }
1233         return ret;
1234 }
1235
1236
1237 /* UIMG */
1238 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1239                 const char *save_as,
1240                 void (*progress_gauge_callback)(long, long), char *cret)
1241 {
1242         register int ret;
1243         char *aaa;
1244
1245         if (!cret) return -1;
1246         if (!save_as) return -1;
1247         if (!path && for_real) return -1;
1248         if (!*path && for_real) return -1;
1249         if (ipc->uploading) return -1;
1250
1251         aaa = (char *)malloc(strlen(save_as) + 17);
1252         if (!aaa) return -1;
1253
1254         sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1255         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1256         free(aaa);
1257         if (ret / 100 == 2 && for_real) {
1258                 ipc->uploading = 1;
1259                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1260                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1261                 ipc->uploading = 0;
1262         }
1263         return ret;
1264 }
1265
1266
1267 /* QUSR */
1268 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1269 {
1270         register int ret;
1271         char *aaa;
1272
1273         if (!cret) return -2;
1274         if (!username) return -2;
1275
1276         aaa = (char *)malloc(strlen(username) + 6);
1277         if (!aaa) return -1;
1278
1279         sprintf(aaa, "QUSR %s", username);
1280         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1281         free(aaa);
1282         return ret;
1283 }
1284
1285
1286 /* LFLR */
1287 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1288 {
1289         size_t bytes;
1290
1291         if (!cret) return -2;
1292         if (!listing) return -2;
1293         if (*listing) return -2;
1294
1295         return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1296 }
1297
1298
1299 /* CFLR */
1300 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1301 {
1302         register int ret;
1303         char *aaa;
1304
1305         if (!cret) return -2;
1306         if (!name) return -2;
1307
1308         aaa = (char *)malloc(strlen(name) + 17);
1309         if (!aaa) return -1;
1310
1311         sprintf(aaa, "CFLR %s|%d", name, for_real);
1312         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1313         free(aaa);
1314         return ret;
1315 }
1316
1317
1318 /* KFLR */
1319 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1320 {
1321         char aaa[27];
1322
1323         if (!cret) return -1;
1324         if (floornum < 0) return -1;
1325
1326         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1327         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1328 }
1329
1330
1331 /* EFLR */
1332 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1333 {
1334         register int ret;
1335         char *aaa;
1336
1337         if (!cret) return -2;
1338         if (!floorname) return -2;
1339         if (floornum < 0) return -2;
1340
1341         aaa = (char *)malloc(strlen(floorname) + 17);
1342         if (!aaa) return -1;
1343
1344         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1345         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1346         free(aaa);
1347         return ret;
1348 }
1349
1350
1351 /* IDEN */
1352 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid, int revision,
1353                 const char *software_name, const char *hostname, char *cret)
1354 {
1355         register int ret;
1356         char *aaa;
1357
1358         if (developerid < 0) return -2;
1359         if (clientid < 0) return -2;
1360         if (revision < 0) return -2;
1361         if (!software_name) return -2;
1362         if (!hostname) return -2;
1363
1364         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1365         if (!aaa) return -1;
1366
1367         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1368                         revision, software_name, hostname);
1369         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1370         free(aaa);
1371         return ret;
1372 }
1373
1374
1375 /* SEXP */
1376 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1377                 char *cret)
1378 {
1379         register int ret;
1380         char *aaa;
1381
1382         if (!cret) return -2;
1383         if (!username) return -2;
1384
1385         aaa = (char *)malloc(strlen(username) + 8);
1386         if (!aaa) return -1;
1387
1388         if (text) {
1389                 sprintf(aaa, "SEXP %s|-", username);
1390                 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1391                                 NULL, NULL, cret);
1392         } else {
1393                 sprintf(aaa, "SEXP %s||", username);
1394                 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1395         }
1396         free(aaa);
1397         return ret;
1398 }
1399
1400
1401 /* GEXP */
1402 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1403 {
1404         size_t bytes;
1405
1406         if (!cret) return -2;
1407         if (!listing) return -2;
1408         if (*listing) return -2;
1409
1410         return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1411 }
1412
1413
1414 /* DEXP */
1415 /* mode is 0 = enable, 1 = disable, 2 = status */
1416 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1417 {
1418         char aaa[16];
1419
1420         if (!cret) return -2;
1421
1422         sprintf(aaa, "DEXP %d", mode);
1423         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1424 }
1425
1426
1427 /* EBIO */
1428 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1429 {
1430         if (!cret) return -2;
1431         if (!bio) return -2;
1432
1433         return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1434                         NULL, NULL, cret);
1435 }
1436
1437
1438 /* RBIO */
1439 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1440 {
1441         register int ret;
1442         size_t bytes;
1443         char *aaa;
1444
1445         if (!cret) return -2;
1446         if (!username) return -2;
1447         if (!listing) return -2;
1448         if (*listing) return -2;
1449
1450         aaa = (char *)malloc(strlen(username) + 6);
1451         if (!aaa) return -1;
1452
1453         sprintf(aaa, "RBIO %s", username);
1454         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1455         free(aaa);
1456         return ret;
1457 }
1458
1459
1460 /* LBIO */
1461 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1462 {
1463         size_t bytes;
1464
1465         if (!cret) return -2;
1466         if (!listing) return -2;
1467         if (*listing) return -2;
1468
1469         return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1470 }
1471
1472
1473 /* STEL */
1474 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1475 {
1476         char aaa[16];
1477
1478         if (!cret) return -1;
1479
1480         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1481         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1482 }
1483
1484
1485 /* TERM */
1486 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1487 {
1488         char aaa[16];
1489
1490         if (!cret) return -1;
1491
1492         sprintf(aaa, "TERM %d", sid);
1493         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1494 }
1495
1496
1497 /* DOWN */
1498 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1499 {
1500         if (!cret) return -1;
1501
1502         return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1503 }
1504
1505
1506 /* SCDN */
1507 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1508 {
1509         char aaa[16];
1510
1511         if (!cret) return -1;
1512
1513         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1514         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1515 }
1516
1517
1518 /* EMSG */
1519 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1520                 char *cret)
1521 {
1522         register int ret;
1523         char *aaa;
1524
1525         if (!cret) return -2;
1526         if (!text) return -2;
1527         if (!filename) return -2;
1528
1529         aaa = (char *)malloc(strlen(filename) + 6);
1530         if (!aaa) return -1;
1531
1532         sprintf(aaa, "EMSG %s", filename);
1533         ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1534         free(aaa);
1535         return ret;
1536 }
1537
1538
1539 /* HCHG */
1540 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1541 {
1542         register int ret;
1543         char *aaa;
1544
1545         if (!cret) return -2;
1546         if (!hostname) return -2;
1547
1548         aaa = (char *)malloc(strlen(hostname) + 6);
1549         if (!aaa) return -1;
1550
1551         sprintf(aaa, "HCHG %s", hostname);
1552         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1553         free(aaa);
1554         return ret;
1555 }
1556
1557
1558 /* RCHG */
1559 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1560 {
1561         register int ret;
1562         char *aaa;
1563
1564         if (!cret) return -2;
1565         if (!roomname) return -2;
1566
1567         aaa = (char *)malloc(strlen(roomname) + 6);
1568         if (!aaa) return -1;
1569
1570         sprintf(aaa, "RCHG %s", roomname);
1571         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1572         free(aaa);
1573         return ret;
1574 }
1575
1576
1577 /* UCHG */
1578 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1579 {
1580         register int ret;
1581         char *aaa;
1582
1583         if (!cret) return -2;
1584         if (!username) return -2;
1585
1586         aaa = (char *)malloc(strlen(username) + 6);
1587         if (!aaa) return -1;
1588
1589         sprintf(aaa, "UCHG %s", username);
1590         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1591         free(aaa);
1592         return ret;
1593 }
1594
1595
1596 /* TIME */
1597 /* This function returns the actual server time reported, or 0 if error */
1598 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1599 {
1600         register time_t tret;
1601         register int ret;
1602
1603         ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1604         if (ret / 100 == 2) {
1605                 tret = extract_long(cret, 0);
1606         } else {
1607                 tret = 0L;
1608         }
1609         return tret;
1610 }
1611
1612
1613 /* AGUP */
1614 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1615                                  struct usersupp **uret, char *cret)
1616 {
1617         register int ret;
1618         char aaa[SIZ];
1619
1620         if (!cret) return -2;
1621         if (!uret) return -2;
1622         if (!*uret) *uret = (struct usersupp *)calloc(1, sizeof(struct usersupp));
1623         if (!*uret) return -1;
1624
1625         sprintf(aaa, "AGUP %s", who);
1626         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1627
1628         if (ret / 100 == 2) {
1629                 extract(uret[0]->fullname, cret, 0);
1630                 extract(uret[0]->password, cret, 1);
1631                 uret[0]->flags = extract_int(cret, 2);
1632                 uret[0]->timescalled = extract_long(cret, 3);
1633                 uret[0]->posted = extract_long(cret, 4);
1634                 uret[0]->axlevel = extract_int(cret, 5);
1635                 uret[0]->usernum = extract_long(cret, 6);
1636                 uret[0]->lastcall = extract_long(cret, 7);
1637                 uret[0]->USuserpurge = extract_int(cret, 8);
1638         }
1639         return ret;
1640 }
1641
1642
1643 /* ASUP */
1644 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct usersupp *uret, char *cret)
1645 {
1646         register int ret;
1647         char *aaa;
1648
1649         if (!cret) return -2;
1650         if (!uret) return -2;
1651
1652         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1653         if (!aaa) return -1;
1654
1655         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1656                         uret->fullname, uret->password, uret->flags,
1657                         uret->timescalled, uret->posted, uret->axlevel,
1658                         uret->usernum, uret->lastcall, uret->USuserpurge);
1659         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1660         free(aaa);
1661         return ret;
1662 }
1663
1664
1665 /* GPEX */
1666 /* which is 0 = room, 1 = floor, 2 = site */
1667 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which, char *cret)
1668 {
1669         static char *proto[] = {"room", "floor", "site"};
1670         char aaa[11];
1671
1672         if (!cret) return -2;
1673         if (which < 0 || which > 2) return -2;
1674         
1675         sprintf(aaa, "GPEX %s", proto[which]);
1676         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1677 }
1678
1679
1680 /* SPEX */
1681 /* which is 0 = room, 1 = floor, 2 = site */
1682 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1683 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which, int policy, int value,
1684                 char *cret)
1685 {
1686         char aaa[38];
1687
1688         if (!cret) return -2;
1689         if (which < 0 || which > 2) return -2;
1690         if (policy < 0 || policy > 3) return -2;
1691         if (policy >= 2 && value < 1) return -2;
1692
1693         sprintf(aaa, "SPEX %d|%d|%d", which, policy, value);
1694         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1695 }
1696
1697
1698 /* CONF GET */
1699 int CtdlGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1700 {
1701         size_t bytes;
1702
1703         if (!cret) return -2;
1704         if (!listing) return -2;
1705         if (*listing) return -2;
1706
1707         return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1708                         listing, &bytes, cret);
1709 }
1710
1711
1712 /* CONF SET */
1713 int CtdlSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1714 {
1715         if (!cret) return -2;
1716         if (!listing) return -2;
1717
1718         return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1719                         NULL, NULL, cret);
1720 }
1721
1722
1723 /* CONF GETSYS */
1724 int CtdlGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1725                 char **listing, char *cret)
1726 {
1727         char *aaa;
1728         size_t bytes;
1729
1730         if (!cret) return -2;
1731         if (!mimetype) return -2;
1732         if (!listing) return -2;
1733         if (*listing) return -2;
1734
1735         aaa = malloc(strlen(mimetype) + 13);
1736         if (!aaa) return -1;
1737         sprintf(aaa, "CONF GETSYS|%s", mimetype);
1738         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1739                         listing, &bytes, cret);
1740 }
1741
1742
1743 /* CONF PUTSYS */
1744 int CtdlSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1745                const char *listing, char *cret)
1746 {
1747         char *aaa;
1748
1749         if (!cret) return -2;
1750         if (!mimetype) return -2;
1751         if (!listing) return -2;
1752
1753         aaa = malloc(strlen(mimetype) + 13);
1754         if (!aaa) return -1;
1755         sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1756         return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1757                         NULL, NULL, cret);
1758 }
1759
1760 /* MMOD */
1761 int CtdlIPCModerateMessage(CtdlIPC *ipc, long msgnum, int level, char *cret)
1762 {
1763         char aaa[27];
1764
1765         if (!cret) return -2;
1766         if (!msgnum) return -2;
1767
1768         sprintf(aaa, "MMOD %ld|%d", msgnum, level);
1769         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1770 }
1771
1772
1773 /* REQT */
1774 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1775 {
1776         char aaa[16];
1777
1778         if (!cret) return -2;
1779         if (session < 0) return -2;
1780
1781         sprintf(aaa, "REQT %d", session);
1782         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1783 }
1784
1785
1786 /* SEEN */
1787 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1788 {
1789         char aaa[27];
1790
1791         if (!cret) return -2;
1792         if (msgnum < 0) return -2;
1793
1794         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1795         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1796 }
1797
1798
1799 /* STLS */
1800 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1801 {
1802         int a;
1803         int r;
1804         char buf[SIZ];
1805
1806 #ifdef HAVE_OPENSSL
1807         SSL *temp_ssl;
1808
1809         /* New SSL object */
1810         temp_ssl = SSL_new(ssl_ctx);
1811         if (!temp_ssl) {
1812                 error_printf("SSL_new failed: %s\n",
1813                                 ERR_reason_error_string(ERR_get_error()));
1814                 return -2;
1815         }
1816         /* Pointless flag waving */
1817 #if SSLEAY_VERSION_NUMBER >= 0x0922
1818         SSL_set_session_id_context(temp_ssl, "Citadel/UX SID", 14);
1819 #endif
1820
1821         if (!access("/var/run/egd-pool", F_OK))
1822                 RAND_egd("/var/run/egd-pool");
1823
1824         if (!RAND_status()) {
1825                 error_printf("PRNG not properly seeded\n");
1826                 return -2;
1827         }
1828
1829         /* Associate network connection with SSL object */
1830         if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1831                 error_printf("SSL_set_fd failed: %s\n",
1832                                 ERR_reason_error_string(ERR_get_error()));
1833                 return -2;
1834         }
1835
1836         if (status_hook != NULL)
1837                 status_hook("Requesting encryption...\r");
1838
1839         /* Ready to start SSL/TLS */
1840         /* Old code
1841         CtdlIPC_putline(ipc, "STLS");
1842         CtdlIPC_getline(ipc, buf);
1843         if (buf[0] != '2') {
1844                 error_printf("Server can't start TLS: %s\n", buf);
1845                 return 0;
1846         }
1847         */
1848         r = CtdlIPCGenericCommand(ipc,
1849                                   "STLS", NULL, 0, NULL, NULL, cret);
1850         if (r / 100 != 2) {
1851                 error_printf("Server can't start TLS: %s\n", buf);
1852                 endtls(temp_ssl);
1853                 return r;
1854         }
1855
1856         /* Do SSL/TLS handshake */
1857         if ((a = SSL_connect(temp_ssl)) < 1) {
1858                 error_printf("SSL_connect failed: %s\n",
1859                                 ERR_reason_error_string(ERR_get_error()));
1860                 endtls(temp_ssl);
1861                 return -2;
1862         }
1863         ipc->ssl = temp_ssl;
1864
1865         BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1866         {
1867                 int bits, alg_bits;
1868
1869                 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1870                 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1871                                 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1872                                 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
1873                                 bits, alg_bits);
1874         }
1875         return r;
1876 #else
1877         return 0;
1878 #endif /* HAVE_OPENSSL */
1879 }
1880
1881
1882 #ifdef HAVE_OPENSSL
1883 static void endtls(SSL *ssl)
1884 {
1885         if (ssl) {
1886                 SSL_shutdown(ssl);
1887                 SSL_free(ssl);
1888         }
1889 }
1890 #endif
1891
1892
1893 /* QDIR */
1894 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
1895 {
1896         char *aaa;
1897
1898         if (!address) return -2;
1899         if (!cret) return -2;
1900
1901         aaa = (char *)malloc(strlen(address) + 6);
1902         if (!aaa) return -1;
1903
1904         sprintf(aaa, "QDIR %s", address);
1905         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1906 }
1907
1908
1909 /* IPGM */
1910 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
1911 {
1912         char aaa[30];
1913
1914         if (!cret) return -2;
1915         sprintf(aaa, "IPGM %d", secret);
1916         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1917 }
1918
1919
1920 /*
1921  * Not implemented:
1922  * 
1923  * CHAT
1924  * ETLS
1925  * EXPI
1926  * GTLS
1927  * IGAB
1928  * MSG3
1929  * MSG4
1930  * NDOP
1931  * NETP
1932  * NUOP
1933  * SMTP
1934  */
1935
1936
1937 /* ************************************************************************** */
1938 /*             Stuff below this line is not for public consumption            */
1939 /* ************************************************************************** */
1940
1941
1942 inline void CtdlIPC_lock(CtdlIPC *ipc)
1943 {
1944 #ifdef THREADED_CLIENT
1945         pthread_mutex_lock(&(ipc->mutex));
1946 #endif
1947 }
1948
1949
1950 inline void CtdlIPC_unlock(CtdlIPC *ipc)
1951 {
1952 #ifdef THREADED_CLIENT
1953         pthread_mutex_unlock(&(ipc->mutex));
1954 #endif
1955 }
1956
1957
1958 /* Read a listing from the server up to 000.  Append to dest if it exists */
1959 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
1960 {
1961         size_t length = 0;
1962         size_t linelength;
1963         char *ret;
1964         char aaa[SIZ];
1965
1966         ret = dest;
1967         if (ret != NULL) {
1968                 length = strlen(ret);
1969         }
1970         else {
1971                 ret = strdup("");
1972                 length = 0;
1973         }
1974
1975         while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
1976                 linelength = strlen(aaa);
1977                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
1978                 if (ret) {
1979                         strcpy(&ret[length], aaa);
1980                         length += linelength;
1981                         strcpy(&ret[length++], "\n");
1982                 }
1983         }
1984
1985         return(ret);
1986 }
1987
1988
1989 /* Send a listing to the server; generate the ending 000. */
1990 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
1991 {
1992         char *text;
1993
1994         text = (char *)malloc(strlen(listing) + 6);
1995         if (text) {
1996                 strcpy(text, listing);
1997                 while (text[strlen(text) - 1] == '\n')
1998                         text[strlen(text) - 1] = '\0';
1999                 strcat(text, "\n000");
2000                 CtdlIPC_putline(ipc, text);
2001                 free(text);
2002                 text = NULL;
2003         } else {
2004                 /* Malloc failed but we are committed to send */
2005                 /* This may result in extra blanks at the bottom */
2006                 CtdlIPC_putline(ipc, text);
2007                 CtdlIPC_putline(ipc, "000");
2008         }
2009         return 0;
2010 }
2011
2012
2013 /* Partial read of file from server */
2014 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2015 {
2016         register size_t len = 0;
2017         char aaa[SIZ];
2018
2019         if (!buf) return -1;
2020         if (!cret) return -1;
2021         if (bytes < 1) return -1;
2022         if (offset < 0) return -1;
2023
2024         CtdlIPC_lock(ipc);
2025         sprintf(aaa, "READ %d|%d", offset, bytes);
2026         CtdlIPC_putline(ipc, aaa);
2027         CtdlIPC_getline(ipc, aaa);
2028         if (aaa[0] != '6')
2029                 strcpy(cret, &aaa[4]);
2030         else {
2031                 len = extract_long(&aaa[4], 0);
2032                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2033                 if (*buf) {
2034                         /* I know what I'm doing */
2035                         serv_read(ipc, (*buf + offset), len);
2036                 } else {
2037                         /* We have to read regardless */
2038                         serv_read(ipc, aaa, len);
2039                         len = -1;
2040                 }
2041         }
2042         CtdlIPC_unlock(ipc);
2043         return len;
2044 }
2045
2046
2047 /* CLOS */
2048 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2049 {
2050         register int ret;
2051
2052         if (!cret) return -2;
2053         if (!ipc->downloading) return -2;
2054
2055         ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2056         if (ret / 100 == 2)
2057                 ipc->downloading = 0;
2058         return ret;
2059 }
2060
2061
2062 /* MSGP */
2063 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2064         register int ret;
2065         char cmd[SIZ];
2066         
2067         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2068         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2069         return ret;
2070 }
2071
2072
2073
2074 /* READ */
2075 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2076                void (*progress_gauge_callback)(long, long), char *cret)
2077 {
2078         register size_t len;
2079
2080         if (!cret) return -1;
2081         if (!buf) return -1;
2082         if (*buf) return -1;
2083         if (!ipc->downloading) return -1;
2084
2085         len = 0;
2086         if (progress_gauge_callback)
2087                 progress_gauge_callback(len, bytes);
2088         while (len < bytes) {
2089                 register size_t block;
2090
2091                 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2092                 if (block == -1) {
2093                         free(*buf);
2094                         return 0;
2095                 }
2096                 len += block;
2097                 if (progress_gauge_callback)
2098                         progress_gauge_callback(len, bytes);
2099         }
2100         return len;
2101 }
2102
2103
2104 /* READ - pipelined */
2105 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2106                void (*progress_gauge_callback)(long, long), char *cret)
2107 {
2108         register size_t len;
2109         register int calls;     /* How many calls in the pipeline */
2110         register int i;         /* iterator */
2111         char aaa[4096];
2112
2113         if (!cret) return -1;
2114         if (!buf) return -1;
2115         if (*buf) return -1;
2116         if (!ipc->downloading) return -1;
2117
2118         *buf = (void *)realloc(*buf, bytes);
2119         if (!*buf) return -1;
2120
2121         len = 0;
2122         CtdlIPC_lock(ipc);
2123         if (progress_gauge_callback)
2124                 progress_gauge_callback(len, bytes);
2125
2126         /* How many calls will be in the pipeline? */
2127         calls = bytes / 4096;
2128         if (bytes % 4096) calls++;
2129
2130         /* Send all requests at once */
2131         for (i = 0; i < calls; i++) {
2132                 sprintf(aaa, "READ %d|4096", i * 4096);
2133                 CtdlIPC_putline(ipc, aaa);
2134         }
2135
2136         /* Receive all responses at once */
2137         for (i = 0; i < calls; i++) {
2138                 CtdlIPC_getline(ipc, aaa);
2139                 if (aaa[0] != '6')
2140                         strcpy(cret, &aaa[4]);
2141                 else {
2142                         len = extract_long(&aaa[4], 0);
2143                         /* I know what I'm doing */
2144                         serv_read(ipc, ((*buf) + (i * 4096)), len);
2145                 }
2146                 if (progress_gauge_callback)
2147                         progress_gauge_callback(i * 4096 + len, bytes);
2148         }
2149         CtdlIPC_unlock(ipc);
2150         return len;
2151 }
2152
2153
2154 /* UCLS */
2155 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2156 {
2157         register int ret;
2158         char cmd[8];
2159
2160         if (!cret) return -1;
2161         if (!ipc->uploading) return -1;
2162
2163         sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2164         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2165         ipc->uploading = 0;
2166         return ret;
2167 }
2168
2169
2170 /* WRIT */
2171 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2172                 void (*progress_gauge_callback)(long, long), char *cret)
2173 {
2174         register int ret = -1;
2175         register size_t offset = 0;
2176         size_t bytes;
2177         char aaa[SIZ];
2178         char buf[4096];
2179         FILE *fd;
2180
2181         if (!cret) return -1;
2182         if (!path) return -1;
2183         if (!*path) return -1;
2184
2185         fd = fopen(path, "r");
2186         if (!fd) return -2;
2187
2188         fseek(fd, 0L, SEEK_END);
2189         bytes = ftell(fd);
2190         rewind(fd);
2191
2192         if (progress_gauge_callback)
2193                 progress_gauge_callback(0, bytes);
2194
2195         while (offset < bytes) {
2196                 register size_t to_write;
2197
2198                 /* Read some data in */
2199                 to_write = fread(buf, 1, 4096, fd);
2200                 if (!to_write) {
2201                         if (feof(fd) || ferror(fd)) break;
2202                 }
2203                 sprintf(aaa, "WRIT %d", to_write);
2204                 CtdlIPC_putline(ipc, aaa);
2205                 CtdlIPC_getline(ipc, aaa);
2206                 strcpy(cret, &aaa[4]);
2207                 ret = atoi(aaa);
2208                 if (aaa[0] == '7') {
2209                         to_write = extract_long(&aaa[4], 0);
2210                         
2211                         serv_write(ipc, buf, to_write);
2212                         offset += to_write;
2213                         if (progress_gauge_callback)
2214                                 progress_gauge_callback(offset, bytes);
2215                         /* Detect short reads and back up if needed */
2216                         fseek(fd, offset, SEEK_SET);
2217                 } else {
2218                         break;
2219                 }
2220         }
2221         if (progress_gauge_callback)
2222                 progress_gauge_callback(1, 1);
2223         return (!ferror(fd) ? ret : -2);
2224 }
2225
2226
2227 /*
2228  * Generic command method.  This method should handle any server command
2229  * except for CHAT.  It takes the following arguments:
2230  *
2231  * ipc                  The server to speak with
2232  * command              Preformatted command to send to server
2233  * to_send              A text or binary file to send to server
2234  *                      (only sent if server requests it)
2235  * bytes_to_send        The number of bytes in to_send (required if
2236  *                      sending binary, optional if sending listing)
2237  * to_receive           Pointer to a NULL pointer, if the server
2238  *                      sends text or binary we will allocate memory
2239  *                      for the file and stuff it here
2240  * bytes_to_receive     If a file is received, we will store its
2241  *                      byte count here
2242  * proto_response       The protocol response.  Caller must provide
2243  *                      this buffer and ensure that it is at least
2244  *                      128 bytes in length.
2245  *
2246  * This function returns a number equal to the protocol response number,
2247  * -1 if an internal error occurred, -2 if caller provided bad values,
2248  * or 0 - the protocol response number if bad values were found during
2249  * the protocol exchange.
2250  * It stores the protocol response string (minus the number) in 
2251  * protocol_response as described above.  Some commands send additional
2252  * data in this string.
2253  */
2254 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2255                 const char *command, const char *to_send,
2256                 size_t bytes_to_send, char **to_receive, 
2257                 size_t *bytes_to_receive, char *proto_response)
2258 {
2259         char buf[SIZ];
2260         register int ret;
2261         int watch_ssl = 0;
2262
2263         if (!command) return -2;
2264         if (!proto_response) return -2;
2265
2266 #ifdef HAVE_OPENSSL
2267         if (ipc->ssl) watch_ssl = 1;
2268 #endif
2269
2270         CtdlIPC_lock(ipc);
2271         CtdlIPC_putline(ipc, command);
2272         while (1) {
2273                 CtdlIPC_getline(ipc, proto_response);
2274                 if (proto_response[3] == '*')
2275                         express_msgs = 1;
2276                 ret = atoi(proto_response);
2277                 strcpy(proto_response, &proto_response[4]);
2278                 switch (ret / 100) {
2279                 default:                        /* Unknown, punt */
2280                 case 2:                         /* OK */
2281                 case 3:                         /* MORE_DATA */
2282                 case 5:                         /* ERROR */
2283                         /* Don't need to do anything */
2284                         break;
2285                 case 1:                         /* LISTING_FOLLOWS */
2286                         if (to_receive && !*to_receive && bytes_to_receive) {
2287                                 *to_receive = CtdlIPCReadListing(ipc, NULL);
2288                         } else { /* Drain */
2289                                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2290                                 ret = -ret;
2291                         }
2292                         break;
2293                 case 4:                         /* SEND_LISTING */
2294                         if (to_send) {
2295                                 CtdlIPCSendListing(ipc, to_send);
2296                         } else {
2297                                 /* No listing given, fake it */
2298                                 CtdlIPC_putline(ipc, "000");
2299                                 ret = -ret;
2300                         }
2301                         break;
2302                 case 6:                         /* BINARY_FOLLOWS */
2303                         if (to_receive && !*to_receive && bytes_to_receive) {
2304                                 *bytes_to_receive =
2305                                         extract_long(proto_response, 0);
2306                                 *to_receive = (char *)
2307                                         malloc((size_t)*bytes_to_receive);
2308                                 if (!*to_receive) {
2309                                         ret = -1;
2310                                 } else {
2311                                         serv_read(ipc, *to_receive,
2312                                                         *bytes_to_receive);
2313                                 }
2314                         } else {
2315                                 /* Drain */
2316                                 size_t drain;
2317
2318                                 drain = extract_long(proto_response, 0);
2319                                 while (drain > SIZ) {
2320                                         serv_read(ipc, buf, SIZ);
2321                                         drain -= SIZ;
2322                                 }
2323                                 serv_read(ipc, buf, drain);
2324                                 ret = -ret;
2325                         }
2326                         break;
2327                 case 7:                         /* SEND_BINARY */
2328                         if (to_send && bytes_to_send) {
2329                                 serv_write(ipc, to_send, bytes_to_send);
2330                         } else if (bytes_to_send) {
2331                                 /* Fake it, send nulls */
2332                                 size_t fake;
2333
2334                                 fake = bytes_to_send;
2335                                 memset(buf, '\0', SIZ);
2336                                 while (fake > SIZ) {
2337                                         serv_write(ipc, buf, SIZ);
2338                                         fake -= SIZ;
2339                                 }
2340                                 serv_write(ipc, buf, fake);
2341                                 ret = -ret;
2342                         } /* else who knows?  DANGER WILL ROBINSON */
2343                         break;
2344                 case 8:                         /* START_CHAT_MODE */
2345                         if (!strncasecmp(command, "CHAT", 4)) {
2346                                 /* Don't call chatmode with generic! */
2347                                 CtdlIPC_putline(ipc, "/quit");
2348                                 ret = -ret;
2349                         } else {
2350                                 /* In this mode we send then receive listing */
2351                                 if (to_send) {
2352                                         CtdlIPCSendListing(ipc, to_send);
2353                                 } else {
2354                                         /* No listing given, fake it */
2355                                         CtdlIPC_putline(ipc, "000");
2356                                         ret = -ret;
2357                                 }
2358                                 if (to_receive && !*to_receive
2359                                                 && bytes_to_receive) {
2360                                         *to_receive = CtdlIPCReadListing(ipc, NULL);
2361                                 } else { /* Drain */
2362                                         while (CtdlIPC_getline(ipc, buf),
2363                                                         strcmp(buf, "000")) ;
2364                                         ret = -ret;
2365                                 }
2366                         }
2367                         break;
2368                 case 9:                         /* ASYNC_MSG */
2369                         /* CtdlIPCDoAsync(ret, proto_response); */
2370                         free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
2371                         break;
2372                 }
2373                 if (ret / 100 != 9)
2374                         break;
2375         }
2376         CtdlIPC_unlock(ipc);
2377         return ret;
2378 }
2379
2380
2381 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2382 {
2383         struct hostent *phe;
2384         struct servent *pse;
2385         struct protoent *ppe;
2386         struct sockaddr_in sin;
2387         int s, type;
2388
2389         memset(&sin, 0, sizeof(sin));
2390         sin.sin_family = AF_INET;
2391
2392         pse = getservbyname(service, protocol);
2393         if (pse != NULL) {
2394                 sin.sin_port = pse->s_port;
2395         }
2396         else if (atoi(service) > 0) {
2397                 sin.sin_port = htons(atoi(service));
2398         }
2399         else {
2400                 sin.sin_port = htons(defaultPort);
2401         }
2402         phe = gethostbyname(host);
2403         if (phe) {
2404                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2405         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2406                 return -1;
2407         }
2408         if ((ppe = getprotobyname(protocol)) == 0) {
2409                 return -1;
2410         }
2411         if (!strcmp(protocol, "udp")) {
2412                 type = SOCK_DGRAM;
2413         } else {
2414                 type = SOCK_STREAM;
2415         }
2416
2417         s = socket(PF_INET, type, ppe->p_proto);
2418         if (s < 0) {
2419                 return -1;
2420         }
2421
2422         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2423                 return -1;
2424         }
2425
2426         return (s);
2427 }
2428
2429 static int uds_connectsock(int *isLocal, char *sockpath)
2430 {
2431         struct sockaddr_un addr;
2432         int s;
2433
2434         memset(&addr, 0, sizeof(addr));
2435         addr.sun_family = AF_UNIX;
2436         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2437
2438         s = socket(AF_UNIX, SOCK_STREAM, 0);
2439         if (s < 0) {
2440                 return -1;
2441         }
2442
2443         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2444                 return -1;
2445         }
2446
2447         *isLocal = 1;
2448         return s;
2449 }
2450
2451
2452 /*
2453  * input binary data from socket
2454  */
2455 static void serv_read(CtdlIPC *ipc, char *buf, int bytes)
2456 {
2457         int len, rlen;
2458
2459 #if defined(HAVE_OPENSSL)
2460         if (ipc->ssl) {
2461                 serv_read_ssl(ipc, buf, bytes);
2462                 return;
2463         }
2464 #endif
2465         len = 0;
2466         while (len < bytes) {
2467                 rlen = read(ipc->sock, &buf[len], bytes - len);
2468                 if (rlen < 1) {
2469                         connection_died(ipc);
2470                         return;
2471                 }
2472                 len += rlen;
2473         }
2474 }
2475
2476
2477 /*
2478  * send binary to server
2479  */
2480 static void serv_write(CtdlIPC *ipc, const char *buf, int nbytes)
2481 {
2482         int bytes_written = 0;
2483         int retval;
2484
2485 #if defined(HAVE_OPENSSL)
2486         if (ipc->ssl) {
2487                 serv_write_ssl(ipc, buf, nbytes);
2488                 return;
2489         }
2490 #endif
2491         while (bytes_written < nbytes) {
2492                 retval = write(ipc->sock, &buf[bytes_written],
2493                                nbytes - bytes_written);
2494                 if (retval < 1) {
2495                         connection_died(ipc);
2496                         return;
2497                 }
2498                 bytes_written += retval;
2499         }
2500 }
2501
2502
2503 #ifdef HAVE_OPENSSL
2504 /*
2505  * input binary data from encrypted connection
2506  */
2507 static void serv_read_ssl(CtdlIPC* ipc, char *buf, int bytes)
2508 {
2509         int len, rlen;
2510         char junk[1];
2511
2512         len = 0;
2513         while (len < bytes) {
2514                 if (SSL_want_read(ipc->ssl)) {
2515                         if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2516                                 error_printf("SSL_write in serv_read:\n");
2517                                 ERR_print_errors_fp(stderr);
2518                         }
2519                 }
2520                 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2521                 if (rlen < 1) {
2522                         long errval;
2523
2524                         errval = SSL_get_error(ipc->ssl, rlen);
2525                         if (errval == SSL_ERROR_WANT_READ ||
2526                                         errval == SSL_ERROR_WANT_WRITE) {
2527                                 sleep(1);
2528                                 continue;
2529                         }
2530                         if (errval == SSL_ERROR_ZERO_RETURN ||
2531                                         errval == SSL_ERROR_SSL) {
2532                                 serv_read(ipc, &buf[len], bytes - len);
2533                                 return;
2534                         }
2535                         error_printf("SSL_read in serv_read:\n");
2536                         ERR_print_errors_fp(stderr);
2537                         connection_died();
2538                         return;
2539                 }
2540                 len += rlen;
2541         }
2542 }
2543
2544
2545 /*
2546  * send binary to server encrypted
2547  */
2548 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, int nbytes)
2549 {
2550         int bytes_written = 0;
2551         int retval;
2552         char junk[1];
2553
2554         while (bytes_written < nbytes) {
2555                 if (SSL_want_write(ipc->ssl)) {
2556                         if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2557                                 error_printf("SSL_read in serv_write:\n");
2558                                 ERR_print_errors_fp(stderr);
2559                         }
2560                 }
2561                 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2562                                 nbytes - bytes_written);
2563                 if (retval < 1) {
2564                         long errval;
2565
2566                         errval = SSL_get_error(ipc->ssl, retval);
2567                         if (errval == SSL_ERROR_WANT_READ ||
2568                                         errval == SSL_ERROR_WANT_WRITE) {
2569                                 sleep(1);
2570                                 continue;
2571                         }
2572                         if (errval == SSL_ERROR_ZERO_RETURN ||
2573                                         errval == SSL_ERROR_SSL) {
2574                                 serv_write(ipc, &buf[bytes_written],
2575                                                 nbytes - bytes_written);
2576                                 return;
2577                         }
2578                         error_printf("SSL_write in serv_write:\n");
2579                         ERR_print_errors_fp(stderr);
2580                         connection_died();
2581                         return;
2582                 }
2583                 bytes_written += retval;
2584         }
2585 }
2586
2587
2588 static void CtdlIPC_init_OpenSSL(void)
2589 {
2590         int a;
2591         SSL_METHOD *ssl_method;
2592         DH *dh;
2593         
2594         /* already done init */
2595         if (ssl_ctx) {
2596                 return;
2597         }
2598
2599         /* Get started */
2600         ssl_ctx = NULL;
2601         dh = NULL;
2602         SSL_load_error_strings();
2603         SSLeay_add_ssl_algorithms();
2604
2605         /* Set up the SSL context in which we will oeprate */
2606         ssl_method = SSLv23_client_method();
2607         ssl_ctx = SSL_CTX_new(ssl_method);
2608         if (!ssl_ctx) {
2609                 error_printf("SSL_CTX_new failed: %s\n",
2610                                 ERR_reason_error_string(ERR_get_error()));
2611                 return;
2612         }
2613         /* Any reasonable cipher we can get */
2614         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2615                 error_printf("No ciphers available for encryption\n");
2616                 return;
2617         }
2618         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2619         
2620         /* Load DH parameters into the context */
2621         dh = DH_new();
2622         if (!dh) {
2623                 error_printf("Can't allocate a DH object: %s\n",
2624                                 ERR_reason_error_string(ERR_get_error()));
2625                 return;
2626         }
2627         if (!(BN_hex2bn(&(dh->p), DH_P))) {
2628                 error_printf("Can't assign DH_P: %s\n",
2629                                 ERR_reason_error_string(ERR_get_error()));
2630                 DH_free(dh);
2631                 return;
2632         }
2633         if (!(BN_hex2bn(&(dh->g), DH_G))) {
2634                 error_printf("Can't assign DH_G: %s\n",
2635                                 ERR_reason_error_string(ERR_get_error()));
2636                 DH_free(dh);
2637                 return;
2638         }
2639         dh->length = DH_L;
2640         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2641         DH_free(dh);
2642
2643 #ifdef THREADED_CLIENT
2644         /* OpenSSL requires callbacks for threaded clients */
2645         CRYPTO_set_locking_callback(ssl_lock);
2646         CRYPTO_set_id_callback(id_callback);
2647
2648         /* OpenSSL requires us to do semaphores for threaded clients */
2649         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2650         if (!Critters) {
2651                 perror("malloc failed");
2652                 exit(1);
2653         } else {
2654                 for (a = 0; a < CRYPTO_num_locks(); a++) {
2655                         Critters[a] = malloc(sizeof (pthread_mutex_t));
2656                         if (!Critters[a]) {
2657                                 perror("malloc failed");
2658                                 exit(1);
2659                         }
2660                         pthread_mutex_init(Critters[a], NULL);
2661                 }
2662         }
2663 #endif /* THREADED_CLIENT */       
2664 }
2665
2666
2667 static void ssl_lock(int mode, int n, const char *file, int line)
2668 {
2669 #ifdef THREADED_CLIENT
2670         if (mode & CRYPTO_LOCK)
2671                 pthread_mutex_lock(Critters[n]);
2672         else
2673                 pthread_mutex_unlock(Critters[n]);
2674 #endif /* THREADED_CLIENT */
2675 }
2676
2677 #ifdef THREADED_CLIENT
2678 static unsigned long id_callback(void) {
2679         return (unsigned long)pthread_self();
2680 }
2681 #endif /* THREADED_CLIENT */
2682 #endif /* HAVE_OPENSSL */
2683
2684
2685 /*
2686  * input string from socket - implemented in terms of serv_read()
2687  */
2688 void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2689 {
2690         int i;
2691
2692         /* Read one character at a time. */
2693         for (i = 0;; i++) {
2694                 serv_read(ipc, &buf[i], 1);
2695                 if (buf[i] == '\n' || i == (SIZ-1))
2696                         break;
2697         }
2698
2699         /* If we got a long line, discard characters until the newline. */
2700         if (i == (SIZ-1))
2701                 while (buf[i] != '\n')
2702                         serv_read(ipc, &buf[i], 1);
2703
2704         /* Strip the trailing newline.
2705          */
2706         buf[i] = 0;
2707 }
2708
2709
2710 /*
2711  * send line to server - implemented in terms of serv_write()
2712  */
2713 void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2714 {
2715         /* error_printf("< %s\n", buf); */
2716         serv_write(ipc, buf, strlen(buf));
2717         serv_write(ipc, "\n", 1);
2718
2719         ipc->last_command_sent = time(NULL);
2720 }
2721
2722
2723 /*
2724  * attach to server
2725  */
2726 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2727 {
2728         int a;
2729         char cithost[SIZ];
2730         char citport[SIZ];
2731         char sockpath[SIZ];
2732
2733         CtdlIPC *ipc = ialloc(CtdlIPC);
2734         if (!ipc) {
2735                 return 0;
2736         }
2737 #if defined(HAVE_OPENSSL)
2738         ipc->ssl = NULL;
2739         CtdlIPC_init_OpenSSL();
2740 #endif
2741 #if defined(HAVE_PTHREAD_H)
2742         pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2743 #endif
2744         ipc->sock = -1;                 /* Not connected */
2745         ipc->isLocal = 0;               /* Not local, of course! */
2746         ipc->downloading = 0;
2747         ipc->uploading = 0;
2748         ipc->last_command_sent = 0L;
2749
2750         strcpy(cithost, DEFAULT_HOST);  /* default host */
2751         strcpy(citport, DEFAULT_PORT);  /* default port */
2752
2753         for (a = 0; a < argc; ++a) {
2754                 if (a == 0) {
2755                         /* do nothing */
2756                 } else if (a == 1) {
2757                         strcpy(cithost, argv[a]);
2758                 } else if (a == 2) {
2759                         strcpy(citport, argv[a]);
2760                 } else {
2761                         error_printf("%s: usage: ",argv[0]);
2762                         error_printf("%s [host] [port] ",argv[0]);
2763                         ifree(ipc);
2764                         errno = EINVAL;
2765                         return 0;
2766                 }
2767         }
2768
2769         if ((!strcmp(cithost, "localhost"))
2770            || (!strcmp(cithost, "127.0.0.1"))) {
2771                 ipc->isLocal = 1;
2772         }
2773
2774         /* If we're using a unix domain socket we can do a bunch of stuff */
2775         if (!strcmp(cithost, UDS)) {
2776                 snprintf(sockpath, sizeof sockpath, "citadel.socket");
2777                 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2778                 if (ipc->sock == -1) {
2779                         ifree(ipc);
2780                         return 0;
2781                 }
2782                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2783                 if (portbuf != NULL) strcpy(portbuf, sockpath);
2784                 return ipc;
2785         }
2786
2787         ipc->sock = connectsock(cithost, citport, "tcp", 504);
2788         if (ipc->sock == -1) {
2789                 ifree(ipc);
2790                 return 0;
2791         }
2792         if (hostbuf != NULL) strcpy(hostbuf, cithost);
2793         if (portbuf != NULL) strcpy(portbuf, citport);
2794         return ipc;
2795 }
2796
2797 /*
2798  * return the file descriptor of the server socket so we can select() on it.
2799  *
2800  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
2801  * rewritten...
2802  */
2803 int CtdlIPC_getsockfd(CtdlIPC* ipc)
2804 {
2805         return ipc->sock;
2806 }
2807
2808
2809 /*
2810  * return one character
2811  *
2812  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
2813  * rewritten...
2814  */
2815 char CtdlIPC_get(CtdlIPC* ipc)
2816 {
2817         char buf[2];
2818         char ch;
2819
2820         serv_read(ipc, buf, 1);
2821         ch = (int) buf[0];
2822
2823         return (ch);
2824 }