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