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