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