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