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