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