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