openssl 1.1 in client
[citadel.git] / textclient / src / citadel_ipc.c
1 /*
2  * Copyright (c) 1987-2018 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         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         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         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         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         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         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         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         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         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         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         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         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         int ret;
576         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         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         time_t tret;
1908         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, struct ctdluser **uret, char *cret)
1922 {
1923         int ret;
1924         char aaa[SIZ];
1925
1926         if (!cret) return -2;
1927         if (!uret) return -2;
1928         if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1929         if (!*uret) return -1;
1930
1931         sprintf(aaa, "AGUP %s", who);
1932         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1933
1934         if (ret / 100 == 2) {
1935                 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1936                 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1937                 uret[0]->flags = extract_int(cret, 2);
1938                 uret[0]->timescalled = extract_long(cret, 3);
1939                 uret[0]->posted = extract_long(cret, 4);
1940                 uret[0]->axlevel = extract_int(cret, 5);
1941                 uret[0]->usernum = extract_long(cret, 6);
1942                 uret[0]->lastcall = extract_long(cret, 7);
1943                 uret[0]->USuserpurge = extract_int(cret, 8);
1944         }
1945         return ret;
1946 }
1947
1948
1949 /* ASUP */
1950 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1951 {
1952         int ret;
1953         char *aaa;
1954
1955         if (!cret) return -2;
1956         if (!uret) return -2;
1957
1958         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1959         if (!aaa) return -1;
1960
1961         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1962                 uret->fullname, uret->password, uret->flags, uret->timescalled,
1963                 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge
1964         );
1965         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1966         free(aaa);
1967         return ret;
1968 }
1969
1970
1971 /* AGEA */
1972 int CtdlIPCAideGetEmailAddresses(CtdlIPC *ipc, const char *who, char *target_buf, char *cret)
1973 {
1974         int ret;
1975         char aaa[SIZ];
1976         char *emailaddrs = NULL;
1977         size_t emailaddrs_len = 0;
1978
1979         sprintf(aaa, "AGEA %s", who);
1980         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
1981
1982         if (ret / 100 == 1) {
1983                 strcpy(target_buf, emailaddrs);
1984         }
1985
1986         if (emailaddrs != NULL) {
1987                 free(emailaddrs);
1988         }
1989
1990         return ret;
1991 }
1992
1993
1994 /* ASEA */
1995 int CtdlIPCAideSetEmailAddresses(CtdlIPC *ipc, const char *who, char *emailaddrs, char *cret)
1996 {
1997         char aaa[SIZ];
1998         int ret;
1999
2000         if (!who) return -2;
2001         if (!emailaddrs) return -2;
2002         if (!cret) return -2;
2003
2004         sprintf(aaa, "ASEA %s", who);
2005         ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2006         return ret;
2007 }
2008
2009
2010 /* GPEX */
2011 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2012 /* caller must free the struct ExpirePolicy */
2013 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
2014                 struct ExpirePolicy **policy, char *cret)
2015 {
2016         static char *proto[] = {
2017                 strof(roompolicy),
2018                 strof(floorpolicy),
2019                 strof(sitepolicy),
2020                 strof(mailboxespolicy)
2021         };
2022         char cmd[256];
2023         int ret;
2024
2025         if (!cret) return -2;
2026         if (!policy) return -2;
2027         if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
2028         if (!*policy) return -1;
2029         if (which < 0 || which > 3) return -2;
2030         
2031         sprintf(cmd, "GPEX %s", proto[which]);
2032         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2033         if (ret / 100 == 2) {
2034                 policy[0]->expire_mode = extract_int(cret, 0);
2035                 policy[0]->expire_value = extract_int(cret, 1);
2036         }
2037         return ret;
2038 }
2039
2040
2041 /* SPEX */
2042 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2043 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2044 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
2045                 struct ExpirePolicy *policy, char *cret)
2046 {
2047         char aaa[38];
2048         char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2049
2050         if (!cret) return -2;
2051         if (which < 0 || which > 3) return -2;
2052         if (!policy) return -2;
2053         if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
2054         if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
2055
2056         sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
2057                         policy->expire_mode, policy->expire_value);
2058         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2059 }
2060
2061
2062 /* CONF GET */
2063 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
2064 {
2065         size_t bytes;
2066
2067         if (!cret) return -2;
2068         if (!listing) return -2;
2069         if (*listing) return -2;
2070
2071         return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
2072                         listing, &bytes, cret);
2073 }
2074
2075
2076 /* CONF SET */
2077 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
2078 {
2079         if (!cret) return -2;
2080         if (!listing) return -2;
2081
2082         return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
2083                         NULL, NULL, cret);
2084 }
2085
2086
2087 /* CONF GETSYS */
2088 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype, char **listing, char *cret)
2089 {
2090         int ret;
2091         char *aaa;
2092         size_t bytes;
2093
2094         if (!cret) return -2;
2095         if (!mimetype) return -2;
2096         if (!listing) return -2;
2097         if (*listing) return -2;
2098
2099         aaa = malloc(strlen(mimetype) + 13);
2100         if (!aaa) return -1;
2101         sprintf(aaa, "CONF GETSYS|%s", mimetype);
2102         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2103         free(aaa);
2104         return ret;
2105 }
2106
2107
2108 /* CONF PUTSYS */
2109 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype, const char *listing, char *cret)
2110 {
2111         int ret;
2112         char *aaa;
2113
2114         if (!cret) return -2;
2115         if (!mimetype) return -2;
2116         if (!listing) return -2;
2117
2118         aaa = malloc(strlen(mimetype) + 13);
2119         if (!aaa) return -1;
2120         sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2121         ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2122         free(aaa);
2123         return ret;
2124 }
2125
2126
2127 /* GNET */
2128 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
2129 {
2130         size_t bytes;
2131
2132         if (!cret) return -2;
2133         if (!listing) return -2;
2134         if (*listing) return -2;
2135
2136         return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2137 }
2138
2139
2140 /* SNET */
2141 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
2142 {
2143         if (!cret) return -2;
2144         if (!listing) return -2;
2145
2146         return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2147 }
2148
2149
2150 /* REQT */
2151 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2152 {
2153         char aaa[64];
2154
2155         if (!cret) return -2;
2156         if (session < 0) return -2;
2157
2158         sprintf(aaa, "REQT %d", session);
2159         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2160 }
2161
2162
2163 /* SEEN */
2164 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2165 {
2166         char aaa[27];
2167
2168         if (!cret) return -2;
2169         if (msgnum < 0) return -2;
2170
2171         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2172         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2173 }
2174
2175
2176 /* STLS */
2177 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2178 {
2179         int a;
2180         int r;
2181         char buf[SIZ];
2182
2183 #ifdef HAVE_OPENSSL
2184         SSL *temp_ssl;
2185
2186         /* New SSL object */
2187         temp_ssl = SSL_new(ssl_ctx);
2188         if (!temp_ssl) {
2189                 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2190                 return -2;
2191         }
2192         /* Pointless flag waving */
2193 #if SSLEAY_VERSION_NUMBER >= 0x0922
2194         SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
2195 #endif
2196
2197         /* Associate network connection with SSL object */
2198         if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2199                 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2200                 return -2;
2201         }
2202
2203         if (status_hook != NULL) {
2204                 status_hook("Requesting encryption...\r");
2205         }
2206
2207         /* Ready to start SSL/TLS */
2208         r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2209         if (r / 100 != 2) {
2210                 error_printf("Server can't start TLS: %s\n", buf);
2211                 endtls(temp_ssl);
2212                 return r;
2213         }
2214
2215         /* Do SSL/TLS handshake */
2216         if ((a = SSL_connect(temp_ssl)) < 1) {
2217                 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2218                 endtls(temp_ssl);
2219                 return -2;
2220         }
2221         ipc->ssl = temp_ssl;
2222
2223         error_printf("Encrypting with %s cipher %s\n",
2224                 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2225                 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2226         );
2227         return r;
2228 #else
2229         return 0;
2230 #endif /* HAVE_OPENSSL */
2231 }
2232
2233
2234 #ifdef HAVE_OPENSSL
2235 static void endtls(SSL *ssl)
2236 {
2237         if (ssl) {
2238                 SSL_shutdown(ssl);
2239                 SSL_free(ssl);
2240         }
2241 }
2242 #endif
2243
2244
2245 /* QDIR */
2246 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2247 {
2248         int ret;
2249         char *aaa;
2250
2251         if (!address) return -2;
2252         if (!cret) return -2;
2253
2254         aaa = (char *)malloc(strlen(address) + 6);
2255         if (!aaa) return -1;
2256
2257         sprintf(aaa, "QDIR %s", address);
2258         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2259         free(aaa);
2260         return ret;
2261 }
2262
2263
2264 /* IPGM */
2265 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2266 {
2267         char aaa[30];
2268
2269         if (!cret) return -2;
2270         sprintf(aaa, "IPGM %d", secret);
2271         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2272 }
2273
2274
2275
2276
2277 /* ************************************************************************** */
2278 /*           Stuff below this line is not for public consumption            */
2279 /* ************************************************************************** */
2280
2281
2282 /* Read a listing from the server up to 000.  Append to dest if it exists */
2283 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2284 {
2285         size_t length = 0;
2286         size_t linelength;
2287         char *ret = NULL;
2288         char aaa[SIZ];
2289
2290         ret = dest;
2291         if (ret != NULL) {
2292                 length = strlen(ret);
2293         } else {
2294                 length = 0;
2295         }
2296
2297         while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2298                 linelength = strlen(aaa);
2299                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2300                 if (ret) {
2301                         strcpy(&ret[length], aaa);
2302                         length += linelength;
2303                         strcpy(&ret[length++], "\n");
2304                 }
2305         }
2306
2307         return(ret);
2308 }
2309
2310
2311 /* Send a listing to the server; generate the ending 000. */
2312 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2313 {
2314         char *text;
2315
2316         text = (char *)malloc(strlen(listing) + 6);
2317         if (text) {
2318                 strcpy(text, listing);
2319                 while (text[strlen(text) - 1] == '\n')
2320                         text[strlen(text) - 1] = '\0';
2321                 strcat(text, "\n000");
2322                 CtdlIPC_putline(ipc, text);
2323                 free(text);
2324                 text = NULL;
2325         } else {
2326                 /* Malloc failed but we are committed to send */
2327                 /* This may result in extra blanks at the bottom */
2328                 CtdlIPC_putline(ipc, text);
2329                 CtdlIPC_putline(ipc, "000");
2330         }
2331         return 0;
2332 }
2333
2334
2335 /* Partial read of file from server */
2336 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2337 {
2338         size_t len = 0;
2339         char aaa[SIZ];
2340
2341         if (!buf) return 0;
2342         if (!cret) return 0;
2343         if (bytes < 1) return 0;
2344
2345         CtdlIPC_lock(ipc);
2346         sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2347         CtdlIPC_putline(ipc, aaa);
2348         CtdlIPC_getline(ipc, aaa);
2349         if (aaa[0] != '6')
2350                 strcpy(cret, &aaa[4]);
2351         else {
2352                 len = extract_long(&aaa[4], 0);
2353                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2354                 if (*buf) {
2355                         /* I know what I'm doing */
2356                         serv_read(ipc, ((char *)(*buf) + offset), len);
2357                 } else {
2358                         /* We have to read regardless */
2359                         serv_read(ipc, aaa, len);
2360                         len = 0;
2361                 }
2362         }
2363         CtdlIPC_unlock(ipc);
2364         return len;
2365 }
2366
2367
2368 /* CLOS */
2369 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2370 {
2371         int ret;
2372
2373         if (!cret) return -2;
2374         if (!ipc->downloading) return -2;
2375
2376         ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2377         if (ret / 100 == 2)
2378                 ipc->downloading = 0;
2379         return ret;
2380 }
2381
2382
2383 /* MSGP */
2384 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2385         int ret;
2386         char cmd[SIZ];
2387         
2388         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2389         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2390         return ret;
2391 }
2392
2393
2394
2395 /* READ */
2396 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2397                 void (*progress_gauge_callback)
2398                         (CtdlIPC*, unsigned long, unsigned long),
2399                char *cret)
2400 {
2401         size_t len;
2402
2403         if (!cret) return -1;
2404         if (!buf) return -1;
2405         if (*buf) return -1;
2406         if (!ipc->downloading) return -1;
2407
2408         len = resume;
2409         if (progress_gauge_callback)
2410                 progress_gauge_callback(ipc, len, bytes);
2411         while (len < bytes) {
2412                 size_t block;
2413
2414                 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2415                 if (block == 0) {
2416                         free(*buf);
2417                         return 0;
2418                 }
2419                 len += block;
2420                 if (progress_gauge_callback)
2421                         progress_gauge_callback(ipc, len, bytes);
2422         }
2423         return len;
2424 }
2425
2426 /* READ - pipelined */
2427 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2428                size_t resume,
2429                 void (*progress_gauge_callback)
2430                         (CtdlIPC*, unsigned long, unsigned long),
2431                char *cret)
2432 {
2433         size_t len;
2434         int calls;      /* How many calls in the pipeline */
2435         int i;          /* iterator */
2436         char aaa[4096];
2437
2438         if (!cret) return -1;
2439         if (!buf) return -1;
2440         if (*buf) return -1;
2441         if (!ipc->downloading) return -1;
2442
2443         *buf = (void *)realloc(*buf, bytes - resume);
2444         if (!*buf) return -1;
2445
2446         len = 0;
2447         CtdlIPC_lock(ipc);
2448         if (progress_gauge_callback)
2449                 progress_gauge_callback(ipc, len, bytes);
2450
2451         /* How many calls will be in the pipeline? */
2452         calls = (bytes - resume) / 4096;
2453         if ((bytes - resume) % 4096) calls++;
2454
2455         /* Send all requests at once */
2456         for (i = 0; i < calls; i++) {
2457                 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2458                 CtdlIPC_putline(ipc, aaa);
2459         }
2460
2461         /* Receive all responses at once */
2462         for (i = 0; i < calls; i++) {
2463                 CtdlIPC_getline(ipc, aaa);
2464                 if (aaa[0] != '6')
2465                         strcpy(cret, &aaa[4]);
2466                 else {
2467                         len = extract_long(&aaa[4], 0);
2468                         /* I know what I'm doing */
2469                         serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2470                 }
2471                 if (progress_gauge_callback)
2472                         progress_gauge_callback(ipc, i * 4096 + len, bytes);
2473         }
2474         CtdlIPC_unlock(ipc);
2475         return len;
2476 }
2477
2478
2479 /* UCLS */
2480 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2481 {
2482         int ret;
2483         char cmd[8];
2484
2485         if (!cret) return -1;
2486         if (!ipc->uploading) return -1;
2487
2488         sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2489         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2490         ipc->uploading = 0;
2491         return ret;
2492 }
2493
2494
2495 /* WRIT */
2496 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2497                 void (*progress_gauge_callback)
2498                         (CtdlIPC*, unsigned long, unsigned long),
2499                 char *cret)
2500 {
2501         int ret = -1;
2502         size_t offset = 0;
2503         size_t bytes;
2504         char aaa[SIZ];
2505         char buf[4096];
2506         FILE *fd = uploadFP;
2507         int ferr;
2508
2509         if (!cret) return -1;
2510
2511         fseek(fd, 0L, SEEK_END);
2512         bytes = ftell(fd);
2513         rewind(fd);
2514
2515         if (progress_gauge_callback)
2516                 progress_gauge_callback(ipc, 0, bytes);
2517
2518         while (offset < bytes) {
2519                 size_t to_write;
2520
2521                 /* Read some data in */
2522                 to_write = fread(buf, 1, 4096, fd);
2523                 if (!to_write) {
2524                         if (feof(fd) || ferror(fd)) break;
2525                 }
2526                 sprintf(aaa, "WRIT %d", (int)to_write);
2527                 CtdlIPC_putline(ipc, aaa);
2528                 CtdlIPC_getline(ipc, aaa);
2529                 strcpy(cret, &aaa[4]);
2530                 ret = atoi(aaa);
2531                 if (aaa[0] == '7') {
2532                         to_write = extract_long(&aaa[4], 0);
2533                         
2534                         serv_write(ipc, buf, to_write);
2535                         offset += to_write;
2536                         if (progress_gauge_callback)
2537                                 progress_gauge_callback(ipc, offset, bytes);
2538                         /* Detect short reads and back up if needed */
2539                         /* offset will never be negative anyway */
2540                         fseek(fd, (signed)offset, SEEK_SET);
2541                 } else {
2542                         break;
2543                 }
2544         }
2545         if (progress_gauge_callback)
2546                 progress_gauge_callback(ipc, 1, 1);
2547         ferr = ferror(fd);
2548         fclose(fd);
2549         return (!ferr ? ret : -2);
2550 }
2551
2552
2553 /*
2554  * Generic command method.  This method should handle any server command
2555  * except for CHAT.  It takes the following arguments:
2556  *
2557  * ipc                  The server to speak with
2558  * command              Preformatted command to send to server
2559  * to_send              A text or binary file to send to server
2560  *                      (only sent if server requests it)
2561  * bytes_to_send        The number of bytes in to_send (required if
2562  *                      sending binary, optional if sending listing)
2563  * to_receive           Pointer to a NULL pointer, if the server
2564  *                      sends text or binary we will allocate memory
2565  *                      for the file and stuff it here
2566  * bytes_to_receive     If a file is received, we will store its
2567  *                      byte count here
2568  * proto_response       The protocol response.  Caller must provide
2569  *                      this buffer and ensure that it is at least
2570  *                      128 bytes in length.
2571  *
2572  * This function returns a number equal to the protocol response number,
2573  * -1 if an internal error occurred, -2 if caller provided bad values,
2574  * or 0 - the protocol response number if bad values were found during
2575  * the protocol exchange.
2576  * It stores the protocol response string (minus the number) in 
2577  * protocol_response as described above.  Some commands send additional
2578  * data in this string.
2579  */
2580 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2581                 const char *command, const char *to_send,
2582                 size_t bytes_to_send, char **to_receive, 
2583                 size_t *bytes_to_receive, char *proto_response)
2584 {
2585         char buf[SIZ];
2586         int ret;
2587
2588         if (!command) return -2;
2589         if (!proto_response) return -2;
2590
2591         CtdlIPC_lock(ipc);
2592         CtdlIPC_putline(ipc, command);
2593         while (1) {
2594                 CtdlIPC_getline(ipc, proto_response);
2595                 if (proto_response[3] == '*')
2596                         instant_msgs = 1;
2597                 ret = atoi(proto_response);
2598                 strcpy(proto_response, &proto_response[4]);
2599                 switch (ret / 100) {
2600                 default:                        /* Unknown, punt */
2601                 case 2:                         /* OK */
2602                 case 3:                         /* MORE_DATA */
2603                 case 5:                         /* ERROR */
2604                         /* Don't need to do anything */
2605                         break;
2606                 case 1:                         /* LISTING_FOLLOWS */
2607                         if (to_receive && !*to_receive && bytes_to_receive) {
2608                                 *to_receive = CtdlIPCReadListing(ipc, NULL);
2609                         } else { /* Drain */
2610                                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2611                                 ret = -ret;
2612                         }
2613                         break;
2614                 case 4:                         /* SEND_LISTING */
2615                         if (to_send) {
2616                                 CtdlIPCSendListing(ipc, to_send);
2617                         } else {
2618                                 /* No listing given, fake it */
2619                                 CtdlIPC_putline(ipc, "000");
2620                                 ret = -ret;
2621                         }
2622                         break;
2623                 case 6:                         /* BINARY_FOLLOWS */
2624                         if (to_receive && !*to_receive && bytes_to_receive) {
2625                                 *bytes_to_receive =
2626                                         extract_long(proto_response, 0);
2627                                 *to_receive = (char *)
2628                                         malloc((size_t)*bytes_to_receive);
2629                                 if (!*to_receive) {
2630                                         ret = -1;
2631                                 } else {
2632                                         serv_read(ipc, *to_receive,
2633                                                         *bytes_to_receive);
2634                                 }
2635                         } else {
2636                                 /* Drain */
2637                                 size_t drain;
2638
2639                                 drain = extract_long(proto_response, 0);
2640                                 while (drain > SIZ) {
2641                                         serv_read(ipc, buf, SIZ);
2642                                         drain -= SIZ;
2643                                 }
2644                                 serv_read(ipc, buf, drain);
2645                                 ret = -ret;
2646                         }
2647                         break;
2648                 case 7:                         /* SEND_BINARY */
2649                         if (to_send && bytes_to_send) {
2650                                 serv_write(ipc, to_send, bytes_to_send);
2651                         } else if (bytes_to_send) {
2652                                 /* Fake it, send nulls */
2653                                 size_t fake;
2654
2655                                 fake = bytes_to_send;
2656                                 memset(buf, '\0', SIZ);
2657                                 while (fake > SIZ) {
2658                                         serv_write(ipc, buf, SIZ);
2659                                         fake -= SIZ;
2660                                 }
2661                                 serv_write(ipc, buf, fake);
2662                                 ret = -ret;
2663                         } /* else who knows?  DANGER WILL ROBINSON */
2664                         break;
2665                 case 8:                         /* START_CHAT_MODE */
2666                         if (!strncasecmp(command, "CHAT", 4)) {
2667                                 /* Don't call chatmode with generic! */
2668                                 CtdlIPC_putline(ipc, "/quit");
2669                                 ret = -ret;
2670                         } else {
2671                                 /* In this mode we send then receive listing */
2672                                 if (to_send) {
2673                                         CtdlIPCSendListing(ipc, to_send);
2674                                 } else {
2675                                         /* No listing given, fake it */
2676                                         CtdlIPC_putline(ipc, "000");
2677                                         ret = -ret;
2678                                 }
2679                                 if (to_receive && !*to_receive
2680                                                 && bytes_to_receive) {
2681                                         *to_receive = CtdlIPCReadListing(ipc, NULL);
2682                                 } else { /* Drain */
2683                                         while (CtdlIPC_getline(ipc, buf),
2684                                                         strcmp(buf, "000")) ;
2685                                         ret = -ret;
2686                                 }
2687                         }
2688                         break;
2689                 case 9:                         /* ASYNC_MSG */
2690                         /* CtdlIPCDoAsync(ret, proto_response); */
2691                         free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
2692                         break;
2693                 }
2694                 if (ret / 100 != 9)
2695                         break;
2696         }
2697         CtdlIPC_unlock(ipc);
2698         return ret;
2699 }
2700
2701
2702 /*
2703  * Connect to a Citadel on a remote host using a TCP/IP socket
2704  */
2705 static int tcp_connectsock(char *host, char *service)
2706 {
2707         struct in6_addr serveraddr;
2708         struct addrinfo hints;
2709         struct addrinfo *res = NULL;
2710         struct addrinfo *ai = NULL;
2711         int rc = (-1);
2712         int sock = (-1);
2713
2714         if ((host == NULL) || IsEmptyStr(host)) {
2715                 service = DEFAULT_HOST ;
2716         }
2717         if ((service == NULL) || IsEmptyStr(service)) {
2718                 service = DEFAULT_PORT ;
2719         }
2720
2721         memset(&hints, 0x00, sizeof(hints));
2722         hints.ai_flags = AI_NUMERICSERV;
2723         hints.ai_family = AF_UNSPEC;
2724         hints.ai_socktype = SOCK_STREAM;
2725
2726         /*
2727          * Handle numeric IPv4 and IPv6 addresses
2728          */
2729         rc = inet_pton(AF_INET, host, &serveraddr);
2730         if (rc == 1) {                                          /* dotted quad */
2731                 hints.ai_family = AF_INET;
2732                 hints.ai_flags |= AI_NUMERICHOST;
2733         }
2734         else {
2735                 rc = inet_pton(AF_INET6, host, &serveraddr);
2736                 if (rc == 1) {                                  /* IPv6 address */
2737                         hints.ai_family = AF_INET6;
2738                         hints.ai_flags |= AI_NUMERICHOST;
2739                 }
2740         }
2741
2742         /* Begin the connection process */
2743
2744         rc = getaddrinfo(host, service, &hints, &res);
2745         if (rc != 0) {
2746                 return(-1);
2747         }
2748
2749         /*
2750          * Try all available addresses until we connect to one or until we run out.
2751          */
2752         for (ai = res; ai != NULL; ai = ai->ai_next) {
2753                 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2754                 if (sock < 0) return(-1);
2755
2756                 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2757                 if (rc >= 0) {
2758                         return(sock);           /* Connected! */
2759                 }
2760                 else {
2761                         close(sock);            /* Failed.  Close the socket to avoid fd leak! */
2762                 }
2763         }
2764
2765         return(-1);
2766 }
2767
2768
2769
2770
2771
2772 /*
2773  * Connect to a Citadel on the local host using a unix domain socket
2774  */
2775 static int uds_connectsock(int *isLocal, char *sockpath)
2776 {
2777         struct sockaddr_un addr;
2778         int s;
2779
2780         memset(&addr, 0, sizeof(addr));
2781         addr.sun_family = AF_UNIX;
2782         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2783
2784         s = socket(AF_UNIX, SOCK_STREAM, 0);
2785         if (s < 0) {
2786                 return -1;
2787         }
2788
2789         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2790                 close(s);
2791                 return -1;
2792         }
2793
2794         *isLocal = 1;
2795         return s;
2796 }
2797
2798
2799 /*
2800  * input binary data from socket
2801  */
2802 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2803 {
2804         unsigned int len, rlen;
2805
2806 #if defined(HAVE_OPENSSL)
2807         if (ipc->ssl) {
2808                 serv_read_ssl(ipc, buf, bytes);
2809                 return;
2810         }
2811 #endif
2812         len = 0;
2813         while (len < bytes) {
2814                 rlen = read(ipc->sock, &buf[len], bytes - len);
2815                 if (rlen < 1) {
2816                         connection_died(ipc, 0);
2817                         return;
2818                 }
2819                 len += rlen;
2820         }
2821 }
2822
2823
2824 /*
2825  * send binary to server
2826  */
2827 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2828 {
2829         unsigned int bytes_written = 0;
2830         int retval;
2831
2832 #if defined(HAVE_OPENSSL)
2833         if (ipc->ssl) {
2834                 serv_write_ssl(ipc, buf, nbytes);
2835                 return;
2836         }
2837 #endif
2838         while (bytes_written < nbytes) {
2839                 retval = write(ipc->sock, &buf[bytes_written],
2840                                nbytes - bytes_written);
2841                 if (retval < 1) {
2842                         connection_died(ipc, 0);
2843                         return;
2844                 }
2845                 bytes_written += retval;
2846         }
2847 }
2848
2849
2850 #ifdef HAVE_OPENSSL
2851 /*
2852  * input binary data from encrypted connection
2853  */
2854 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2855 {
2856         int len, rlen;
2857         char junk[1];
2858
2859         len = 0;
2860         while (len < bytes) {
2861                 if (SSL_want_read(ipc->ssl)) {
2862                         if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2863                                 error_printf("SSL_write in serv_read:\n");
2864                                 ERR_print_errors_fp(stderr);
2865                         }
2866                 }
2867                 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2868                 if (rlen < 1) {
2869                         long errval;
2870
2871                         errval = SSL_get_error(ipc->ssl, rlen);
2872                         if (errval == SSL_ERROR_WANT_READ ||
2873                                         errval == SSL_ERROR_WANT_WRITE) {
2874                                 sleep(1);
2875                                 continue;
2876                         }
2877 /***
2878  Not sure why we'd want to handle these error codes any differently,
2879  but this definitely isn't the way to handle them.  Someone must have
2880  naively assumed that we could fall back to unencrypted communications,
2881  but all it does is just recursively blow the stack.
2882                         if (errval == SSL_ERROR_ZERO_RETURN ||
2883                                         errval == SSL_ERROR_SSL) {
2884                                 serv_read(ipc, &buf[len], bytes - len);
2885                                 return;
2886                         }
2887  ***/
2888                         error_printf("SSL_read in serv_read: %s\n",
2889                                         ERR_reason_error_string(ERR_peek_error()));
2890                         connection_died(ipc, 1);
2891                         return;
2892                 }
2893                 len += rlen;
2894         }
2895 }
2896
2897
2898 /*
2899  * send binary to server encrypted
2900  */
2901 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2902 {
2903         unsigned int bytes_written = 0;
2904         int retval;
2905         char junk[1];
2906
2907         while (bytes_written < nbytes) {
2908                 if (SSL_want_write(ipc->ssl)) {
2909                         if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2910                                 error_printf("SSL_read in serv_write:\n");
2911                                 ERR_print_errors_fp(stderr);
2912                         }
2913                 }
2914                 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2915                                 nbytes - bytes_written);
2916                 if (retval < 1) {
2917                         long errval;
2918
2919                         errval = SSL_get_error(ipc->ssl, retval);
2920                         if (errval == SSL_ERROR_WANT_READ ||
2921                                         errval == SSL_ERROR_WANT_WRITE) {
2922                                 sleep(1);
2923                                 continue;
2924                         }
2925                         if (errval == SSL_ERROR_ZERO_RETURN ||
2926                                         errval == SSL_ERROR_SSL) {
2927                                 serv_write(ipc, &buf[bytes_written],
2928                                                 nbytes - bytes_written);
2929                                 return;
2930                         }
2931                         error_printf("SSL_write in serv_write: %s\n",
2932                                         ERR_reason_error_string(ERR_peek_error()));
2933                         connection_died(ipc, 1);
2934                         return;
2935                 }
2936                 bytes_written += retval;
2937         }
2938 }
2939
2940
2941 #ifdef THREADED_CLIENT
2942 static void ssl_lock(int mode, int n, const char *file, int line)
2943 {
2944         if (mode & CRYPTO_LOCK)
2945                 pthread_mutex_lock(Critters[n]);
2946         else
2947                 pthread_mutex_unlock(Critters[n]);
2948 }
2949 #endif /* THREADED_CLIENT */
2950
2951
2952 static void CtdlIPC_init_OpenSSL(void)
2953 {
2954         int a;
2955         const SSL_METHOD *ssl_method;
2956         DH *dh;
2957         
2958         /* already done init */
2959         if (ssl_ctx) {
2960                 return;
2961         }
2962
2963         /* Get started */
2964         a = 0;
2965         ssl_ctx = NULL;
2966         dh = NULL;
2967         SSL_load_error_strings();
2968         SSLeay_add_ssl_algorithms();
2969
2970         /* Set up the SSL context in which we will oeprate */
2971         ssl_method = SSLv23_client_method();
2972         ssl_ctx = SSL_CTX_new(ssl_method);
2973         if (!ssl_ctx) {
2974                 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2975                 return;
2976         }
2977         /* Any reasonable cipher we can get */
2978         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2979                 error_printf("No ciphers available for encryption\n");
2980                 return;
2981         }
2982         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2983
2984         /* Load DH parameters into the context */
2985         dh = DH_new();
2986         if (!dh) {
2987                 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
2988                 return;
2989         }
2990
2991         if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
2992                 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
2993                 DH_free(dh);
2994                 return;
2995         }
2996
2997         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2998         DH_free(dh);
2999
3000 #ifdef THREADED_CLIENT
3001         /* OpenSSL requires callbacks for threaded clients */
3002         CRYPTO_set_locking_callback(ssl_lock);
3003         CRYPTO_set_id_callback(id_callback);
3004
3005         /* OpenSSL requires us to do semaphores for threaded clients */
3006         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
3007         if (!Critters) {
3008                 perror("malloc failed");
3009                 exit(1);
3010         } else {
3011                 for (a = 0; a < CRYPTO_num_locks(); a++) {
3012                         Critters[a] = malloc(sizeof (pthread_mutex_t));
3013                         if (!Critters[a]) {
3014                                 perror("malloc failed");
3015                                 exit(1);
3016                         }
3017                         pthread_mutex_init(Critters[a], NULL);
3018                 }
3019         }
3020 #endif /* THREADED_CLIENT */       
3021 }
3022
3023
3024
3025 #ifdef THREADED_CLIENT
3026 static unsigned long id_callback(void) {
3027         return (unsigned long)pthread_self();
3028 }
3029 #endif /* THREADED_CLIENT */
3030 #endif /* HAVE_OPENSSL */
3031
3032
3033 int
3034 ReadNetworkChunk(CtdlIPC* ipc)
3035 {
3036         fd_set read_fd;
3037         int ret = 0;
3038         int err = 0;
3039         struct timeval tv;
3040         size_t n;
3041
3042         tv.tv_sec = 1;
3043         tv.tv_usec = 1000;
3044         /*tries = 0; */
3045         n = 0;
3046         while (1)
3047         {
3048                 errno=0;
3049                 FD_ZERO(&read_fd);
3050                 FD_SET(ipc->sock, &read_fd);
3051                 ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
3052                 
3053                 if (ret > 0) {
3054                         
3055                         *(ipc->BufPtr) = '\0';
3056                         n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1, 0);
3057                         if (n > 0) {
3058                                 ipc->BufPtr[n]='\0';
3059                                 ipc->BufUsed += n;
3060                                 return n;
3061                         }
3062                         else 
3063                                 return n;
3064                 }
3065                 else if (ret < 0) {
3066                         if (!(errno == EINTR || errno == EAGAIN))
3067                                 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
3068                         return -1;
3069                 }/*
3070                 else {
3071                         tries ++;
3072                         if (tries >= 10)
3073                         n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
3074                         if (n > 0) {
3075                                 ipc->BufPtr[n]='\0';
3076                                 ipc->BufUsed += n;
3077                                 return n;
3078                         }
3079                         else {
3080                                 connection_died(ipc, 0);
3081                                 return -1;
3082                         }
3083                         }*/
3084         }
3085 }
3086
3087 /*
3088  * input string from socket - implemented in terms of serv_read()
3089  */
3090 #ifdef CHUNKED_READ
3091
3092 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3093 {
3094         int i, ntries;
3095         char *aptr, *bptr, *aeptr, *beptr;
3096
3097 //      error_printf("---\n");
3098
3099         beptr = buf + SIZ;
3100 #if defined(HAVE_OPENSSL)
3101         if (ipc->ssl) {
3102                 
3103                 /* Read one character at a time. */
3104                 for (i = 0;; i++) {
3105                         serv_read(ipc, &buf[i], 1);
3106                         if (buf[i] == '\n' || i == (SIZ-1))
3107                                 break;
3108                 }
3109                 
3110                 /* If we got a long line, discard characters until the newline. */
3111                 if (i == (SIZ-1))
3112                         while (buf[i] != '\n')
3113                                 serv_read(ipc, &buf[i], 1);
3114                 
3115                 /* Strip the trailing newline (and carriage return, if present) */
3116                 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3117                 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3118         }
3119         else
3120 #endif
3121         {
3122                 if (ipc->Buf == NULL)
3123                 {
3124                         ipc->BufSize = SIZ;
3125                         ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3126                         *(ipc->Buf) = '\0';
3127                         ipc->BufPtr = ipc->Buf;
3128                 }
3129
3130                 ntries = 0;
3131 //              while ((ipc->BufUsed == 0)||(ntries++ > 10))
3132                 if (ipc->BufUsed == 0)
3133                         ReadNetworkChunk(ipc);
3134
3135 ////            if (ipc->BufUsed != 0) while (1)
3136                 bptr = buf;
3137
3138                 while (1)
3139                 {
3140                         aptr = ipc->BufPtr;
3141                         aeptr = ipc->Buf + ipc->BufSize;
3142                         while ((aptr < aeptr) && 
3143                                (bptr < beptr) &&
3144                                (*aptr != '\0') && 
3145                                (*aptr != '\n'))
3146                                 *(bptr++) = *(aptr++);
3147                         if ((*aptr == '\n') && (aptr < aeptr))
3148                         {
3149                                 /* Terminate it right, remove the line breaks */
3150                                 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3151                                         aptr ++;
3152                                 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3153                                         aptr ++;
3154                                 *(bptr++) = '\0';
3155 //                              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);
3156                                 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3157                                         *(--bptr) = '\0';
3158                                 
3159                                 /* is there more in the buffer we need to read later? */
3160                                 if (ipc->Buf + ipc->BufUsed > aptr)
3161                                 {
3162                                         ipc->BufPtr = aptr;
3163                                 }
3164                                 else
3165                                 {
3166                                         ipc->BufUsed = 0;
3167                                         ipc->BufPtr = ipc->Buf;
3168                                 }
3169 //                              error_printf("----bla6\n");
3170                                 return;
3171                                 
3172                         }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3173                         else if ((ipc->BufPtr != ipc->Buf) && 
3174                                  (ipc->BufUsed > (ipc->BufSize  - (ipc->BufSize / 4))))
3175                         {
3176                                 size_t NewBufSize = ipc->BufSize * 2;
3177                                 int delta = (ipc->BufPtr - ipc->Buf);
3178                                 char *NewBuf;
3179
3180                                 /* if the line would end after our buffer, we should use a bigger buffer. */
3181                                 NewBuf = (char *)malloc (NewBufSize + 10);
3182                                 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3183                                 free(ipc->Buf);
3184                                 ipc->Buf = ipc->BufPtr = NewBuf;
3185                                 ipc->BufUsed -= delta;
3186                                 ipc->BufSize = NewBufSize;
3187                         }
3188                         if (ReadNetworkChunk(ipc) <0)
3189                         {
3190 //                              error_printf("----bla\n");
3191                                 return;
3192                         }
3193                 }
3194 ///             error_printf("----bl45761%s\nipc->BufUsed");
3195         }
3196 //      error_printf("----bla1\n");
3197 }
3198
3199 #else   /* CHUNKED_READ */
3200
3201 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3202 {
3203         int i;
3204
3205         /* Read one character at a time. */
3206         for (i = 0;; i++) {
3207                 serv_read(ipc, &buf[i], 1);
3208                 if (buf[i] == '\n' || i == (SIZ-1))
3209                         break;
3210         }
3211
3212         /* If we got a long line, discard characters until the newline. */
3213         if (i == (SIZ-1))
3214                 while (buf[i] != '\n')
3215                         serv_read(ipc, &buf[i], 1);
3216
3217         /* Strip the trailing newline (and carriage return, if present) */
3218         if (i>=0 && buf[i] == 10) buf[i--] = 0;
3219         if (i>=0 && buf[i] == 13) buf[i--] = 0;
3220 }
3221
3222
3223 #endif  /* CHUNKED_READ */
3224
3225
3226 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3227 {
3228         CtdlIPC_getline(ipc, buf);
3229 }
3230
3231 /*
3232  * send line to server - implemented in terms of serv_write()
3233  */
3234 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3235 {
3236         char *cmd = NULL;
3237         int len;
3238
3239         len = strlen(buf);
3240         cmd = malloc(len + 2);
3241         if (!cmd) {
3242                 /* This requires no extra memory */
3243                 serv_write(ipc, buf, len);
3244                 serv_write(ipc, "\n", 1);
3245         } else {
3246                 /* This is network-optimized */
3247                 strncpy(cmd, buf, len);
3248                 strcpy(cmd + len, "\n");
3249                 serv_write(ipc, cmd, len + 1);
3250                 free(cmd);
3251         }
3252
3253         ipc->last_command_sent = time(NULL);
3254 }
3255
3256 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3257 {
3258         CtdlIPC_putline(ipc, buf);
3259 }
3260
3261
3262 /*
3263  * attach to server
3264  */
3265 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3266 {
3267         int a;
3268         char cithost[SIZ];
3269         char citport[SIZ];
3270         char sockpath[SIZ];
3271         CtdlIPC* ipc;
3272
3273         ipc = ialloc(CtdlIPC);
3274         if (!ipc) {
3275                 return 0;
3276         }
3277 #if defined(HAVE_OPENSSL)
3278         ipc->ssl = NULL;
3279         CtdlIPC_init_OpenSSL();
3280 #endif
3281 #if defined(HAVE_PTHREAD_H)
3282         pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3283 #endif
3284         ipc->sock = -1;                 /* Not connected */
3285         ipc->isLocal = 0;               /* Not local, of course! */
3286         ipc->downloading = 0;
3287         ipc->uploading = 0;
3288         ipc->last_command_sent = 0L;
3289         ipc->network_status_cb = NULL;
3290         ipc->Buf = NULL;
3291         ipc->BufUsed = 0;
3292         ipc->BufPtr = NULL;
3293
3294         strcpy(cithost, DEFAULT_HOST);  /* default host */
3295         strcpy(citport, DEFAULT_PORT);  /* default port */
3296
3297         /* Allow caller to supply our values */
3298         if (hostbuf && strlen(hostbuf) > 0) {
3299                 strcpy(cithost, hostbuf);
3300         }
3301         if (portbuf && strlen(portbuf) > 0) {
3302                 strcpy(citport, portbuf);
3303         }
3304
3305         /* Read host/port from command line if present */
3306         for (a = 0; a < argc; ++a) {
3307                 if (a == 0) {
3308                         /* do nothing */
3309                 } else if (a == 1) {
3310                         strcpy(cithost, argv[a]);
3311                 } else if (a == 2) {
3312                         strcpy(citport, argv[a]);
3313                 } else {
3314                         error_printf("%s: usage: ",argv[0]);
3315                         error_printf("%s [host] [port] ",argv[0]);
3316                         ifree(ipc);
3317                         errno = EINVAL;
3318                         return 0;
3319                 }
3320         }
3321
3322         if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3323                 ipc->isLocal = 1;
3324         }
3325
3326         /* If we're using a unix domain socket we can do a bunch of stuff */
3327         if (!strcmp(cithost, UDS)) {
3328                 if (!strcasecmp(citport, DEFAULT_PORT)) {
3329                         snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3330                 }
3331                 else {
3332                         snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3333                 }
3334                 printf("[%s]\n", sockpath);
3335                 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3336                 if (ipc->sock == -1) {
3337                         ifree(ipc);
3338                         return 0;
3339                 }
3340                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3341                 if (portbuf != NULL) strcpy(portbuf, sockpath);
3342                 strcpy(ipc->ip_hostname, "");
3343                 strcpy(ipc->ip_address, "");
3344                 return ipc;
3345         }
3346
3347         printf("[%s:%s]\n", cithost, citport);
3348         ipc->sock = tcp_connectsock(cithost, citport);
3349         if (ipc->sock == -1) {
3350                 ifree(ipc);
3351                 return 0;
3352         }
3353
3354
3355         /* Learn the actual network identity of the host to which we are connected */
3356
3357         struct sockaddr_in6 clientaddr;
3358         unsigned int addrlen = sizeof(clientaddr);
3359
3360         ipc->ip_hostname[0] = 0;
3361         ipc->ip_address[0] = 0;
3362
3363         getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3364         getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3365                 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3366         );
3367         getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3368                 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3369         );
3370
3371         /* stuff other things elsewhere */
3372
3373         if (hostbuf != NULL) strcpy(hostbuf, cithost);
3374         if (portbuf != NULL) strcpy(portbuf, citport);
3375         return ipc;
3376 }
3377
3378
3379 /*
3380  * Disconnect and delete the IPC class (destructor)
3381  */
3382 void CtdlIPC_delete(CtdlIPC* ipc)
3383 {
3384 #ifdef HAVE_OPENSSL
3385         if (ipc->ssl) {
3386                 SSL_shutdown(ipc->ssl);
3387                 SSL_free(ipc->ssl);
3388                 ipc->ssl = NULL;
3389         }
3390 #endif
3391         if (ipc->sock > -1) {
3392                 shutdown(ipc->sock, 2); /* Close it up */
3393                 ipc->sock = -1;
3394         }
3395         if (ipc->Buf != NULL)
3396                 free (ipc->Buf);
3397         ipc->Buf = NULL;
3398         ipc->BufPtr = NULL;
3399         ifree(ipc);
3400 }
3401
3402
3403 /*
3404  * Disconnect and delete the IPC class (destructor)
3405  * Also NULLs out the pointer
3406  */
3407 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3408 {
3409         CtdlIPC_delete(*pipc);
3410         *pipc = NULL;
3411 }
3412
3413
3414 /*
3415  * return the file descriptor of the server socket so we can select() on it.
3416  *
3417  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3418  * rewritten...
3419  */
3420 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3421 {
3422         return ipc->sock;
3423 }
3424
3425
3426 /*
3427  * return one character
3428  *
3429  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3430  * rewritten...
3431  */
3432 char CtdlIPC_get(CtdlIPC* ipc)
3433 {
3434         char buf[2];
3435         char ch;
3436
3437         serv_read(ipc, buf, 1);
3438         ch = (int) buf[0];
3439
3440         return (ch);
3441 }