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