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