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