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