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