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