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