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