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