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