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