HUGE PATCH. This moves all of mime_parser.c and all
[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
1338         if (!cret) return -1;
1339         if (!save_as) return -1;
1340         if (!comment) return -1;
1341         if (!path) return -1;
1342         if (!*path) return -1;
1343         if (ipc->uploading) return -1;
1344
1345         aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1346         if (!aaa) return -1;
1347
1348         sprintf(aaa, "UOPN %s|%s", save_as, comment);
1349         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1350         free(aaa);
1351         if (ret / 100 == 2) {
1352                 ipc->uploading = 1;
1353                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1354                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1355                 ipc->uploading = 0;
1356         }
1357         return ret;
1358 }
1359
1360
1361 /* UIMG */
1362 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1363                 const char *save_as,
1364                 void (*progress_gauge_callback)
1365                         (CtdlIPC*, unsigned long, unsigned long),
1366                 char *cret)
1367 {
1368         register int ret;
1369         char *aaa;
1370
1371         if (!cret) return -1;
1372         if (!save_as) return -1;
1373         if (!path && for_real) return -1;
1374         if (!*path && for_real) return -1;
1375         if (ipc->uploading) return -1;
1376
1377         aaa = (char *)malloc(strlen(save_as) + 17);
1378         if (!aaa) return -1;
1379
1380         sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1381         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1382         free(aaa);
1383         if (ret / 100 == 2 && for_real) {
1384                 ipc->uploading = 1;
1385                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1386                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1387                 ipc->uploading = 0;
1388         }
1389         return ret;
1390 }
1391
1392
1393 /* QUSR */
1394 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1395 {
1396         register int ret;
1397         char *aaa;
1398
1399         if (!cret) return -2;
1400         if (!username) return -2;
1401
1402         aaa = (char *)malloc(strlen(username) + 6);
1403         if (!aaa) return -1;
1404
1405         sprintf(aaa, "QUSR %s", username);
1406         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1407         free(aaa);
1408         return ret;
1409 }
1410
1411
1412 /* LFLR */
1413 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1414 {
1415         size_t bytes;
1416
1417         if (!cret) return -2;
1418         if (!listing) return -2;
1419         if (*listing) return -2;
1420
1421         return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1422 }
1423
1424
1425 /* CFLR */
1426 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1427 {
1428         register int ret;
1429         char aaa[SIZ];
1430
1431         if (!cret) return -2;
1432         if (!name) return -2;
1433
1434         sprintf(aaa, "CFLR %s|%d", name, for_real);
1435         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1436         return ret;
1437 }
1438
1439
1440 /* KFLR */
1441 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1442 {
1443         char aaa[SIZ];
1444
1445         if (!cret) return -1;
1446         if (floornum < 0) return -1;
1447
1448         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1449         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1450 }
1451
1452
1453 /* EFLR */
1454 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1455 {
1456         register int ret;
1457         char aaa[SIZ];
1458
1459         if (!cret) return -2;
1460         if (!floorname) return -2;
1461         if (floornum < 0) return -2;
1462
1463         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1464         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1465         return ret;
1466 }
1467
1468
1469 /*
1470  * IDEN 
1471  *
1472  * You only need to fill out hostname, the defaults will be used if any of the
1473  * other fields are not set properly.
1474  */
1475 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1476                 int revision, const char *software_name, const char *hostname,
1477                 char *cret)
1478 {
1479         register int ret;
1480         char *aaa;
1481
1482         if (developerid < 0 || clientid < 0 || revision < 0 ||
1483             !software_name) {
1484                 developerid = 8;
1485                 clientid = 0;
1486                 revision = REV_LEVEL - 600;
1487                 software_name = "Citadel (libcitadel)";
1488         }
1489         if (!hostname) return -2;
1490
1491         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1492         if (!aaa) return -1;
1493
1494         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1495                         revision, software_name, hostname);
1496         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1497         free(aaa);
1498         return ret;
1499 }
1500
1501
1502 /* SEXP */
1503 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1504                 char *cret)
1505 {
1506         register int ret;
1507         char *aaa;
1508
1509         if (!cret) return -2;
1510         if (!username) return -2;
1511
1512         aaa = (char *)malloc(strlen(username) + 8);
1513         if (!aaa) return -1;
1514
1515         if (text) {
1516                 sprintf(aaa, "SEXP %s|-", username);
1517                 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1518                                 NULL, NULL, cret);
1519         } else {
1520                 sprintf(aaa, "SEXP %s||", username);
1521                 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1522         }
1523         free(aaa);
1524         return ret;
1525 }
1526
1527
1528 /* GEXP */
1529 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1530 {
1531         size_t bytes;
1532
1533         if (!cret) return -2;
1534         if (!listing) return -2;
1535         if (*listing) return -2;
1536
1537         return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1538 }
1539
1540
1541 /* DEXP */
1542 /* mode is 0 = enable, 1 = disable, 2 = status */
1543 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1544 {
1545         char aaa[16];
1546
1547         if (!cret) return -2;
1548
1549         sprintf(aaa, "DEXP %d", mode);
1550         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1551 }
1552
1553
1554 /* EBIO */
1555 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1556 {
1557         if (!cret) return -2;
1558         if (!bio) return -2;
1559
1560         return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1561                         NULL, NULL, cret);
1562 }
1563
1564
1565 /* RBIO */
1566 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1567 {
1568         register int ret;
1569         size_t bytes;
1570         char *aaa;
1571
1572         if (!cret) return -2;
1573         if (!username) return -2;
1574         if (!listing) return -2;
1575         if (*listing) return -2;
1576
1577         aaa = (char *)malloc(strlen(username) + 6);
1578         if (!aaa) return -1;
1579
1580         sprintf(aaa, "RBIO %s", username);
1581         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1582         free(aaa);
1583         return ret;
1584 }
1585
1586
1587 /* LBIO */
1588 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1589 {
1590         size_t bytes;
1591
1592         if (!cret) return -2;
1593         if (!listing) return -2;
1594         if (*listing) return -2;
1595
1596         return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1597 }
1598
1599
1600 /* STEL */
1601 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1602 {
1603         char aaa[16];
1604
1605         if (!cret) return -1;
1606
1607         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1608         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1609 }
1610
1611
1612 /* TERM */
1613 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1614 {
1615         char aaa[16];
1616
1617         if (!cret) return -1;
1618
1619         sprintf(aaa, "TERM %d", sid);
1620         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1621 }
1622
1623
1624 /* DOWN */
1625 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1626 {
1627         if (!cret) return -1;
1628
1629         return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1630 }
1631
1632
1633 /* SCDN */
1634 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1635 {
1636         char aaa[16];
1637
1638         if (!cret) return -1;
1639
1640         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1641         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1642 }
1643
1644
1645 /* EMSG */
1646 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1647                 char *cret)
1648 {
1649         register int ret;
1650         char *aaa;
1651
1652         if (!cret) return -2;
1653         if (!text) return -2;
1654         if (!filename) return -2;
1655
1656         aaa = (char *)malloc(strlen(filename) + 6);
1657         if (!aaa) return -1;
1658
1659         sprintf(aaa, "EMSG %s", filename);
1660         ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1661         free(aaa);
1662         return ret;
1663 }
1664
1665
1666 /* HCHG */
1667 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1668 {
1669         register int ret;
1670         char *aaa;
1671
1672         if (!cret) return -2;
1673         if (!hostname) return -2;
1674
1675         aaa = (char *)malloc(strlen(hostname) + 6);
1676         if (!aaa) return -1;
1677
1678         sprintf(aaa, "HCHG %s", hostname);
1679         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1680         free(aaa);
1681         return ret;
1682 }
1683
1684
1685 /* RCHG */
1686 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1687 {
1688         register int ret;
1689         char *aaa;
1690
1691         if (!cret) return -2;
1692         if (!roomname) return -2;
1693
1694         aaa = (char *)malloc(strlen(roomname) + 6);
1695         if (!aaa) return -1;
1696
1697         sprintf(aaa, "RCHG %s", roomname);
1698         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1699         free(aaa);
1700         return ret;
1701 }
1702
1703
1704 /* UCHG */
1705 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1706 {
1707         register int ret;
1708         char *aaa;
1709
1710         if (!cret) return -2;
1711         if (!username) return -2;
1712
1713         aaa = (char *)malloc(strlen(username) + 6);
1714         if (!aaa) return -1;
1715
1716         sprintf(aaa, "UCHG %s", username);
1717         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1718         free(aaa);
1719         return ret;
1720 }
1721
1722
1723 /* TIME */
1724 /* This function returns the actual server time reported, or 0 if error */
1725 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1726 {
1727         register time_t tret;
1728         register int ret;
1729
1730         ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1731         if (ret / 100 == 2) {
1732                 tret = extract_long(cret, 0);
1733         } else {
1734                 tret = 0L;
1735         }
1736         return tret;
1737 }
1738
1739
1740 /* AGUP */
1741 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1742                                  struct ctdluser **uret, char *cret)
1743 {
1744         register int ret;
1745         char aaa[SIZ];
1746
1747         if (!cret) return -2;
1748         if (!uret) return -2;
1749         if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1750         if (!*uret) return -1;
1751
1752         sprintf(aaa, "AGUP %s", who);
1753         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1754
1755         if (ret / 100 == 2) {
1756                 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1757                 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1758                 uret[0]->flags = extract_int(cret, 2);
1759                 uret[0]->timescalled = extract_long(cret, 3);
1760                 uret[0]->posted = extract_long(cret, 4);
1761                 uret[0]->axlevel = extract_int(cret, 5);
1762                 uret[0]->usernum = extract_long(cret, 6);
1763                 uret[0]->lastcall = extract_long(cret, 7);
1764                 uret[0]->USuserpurge = extract_int(cret, 8);
1765         }
1766         return ret;
1767 }
1768
1769
1770 /* ASUP */
1771 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1772 {
1773         register int ret;
1774         char *aaa;
1775
1776         if (!cret) return -2;
1777         if (!uret) return -2;
1778
1779         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1780         if (!aaa) return -1;
1781
1782         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1783                         uret->fullname, uret->password, uret->flags,
1784                         uret->timescalled, uret->posted, uret->axlevel,
1785                         uret->usernum, uret->lastcall, uret->USuserpurge);
1786         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1787         free(aaa);
1788         return ret;
1789 }
1790
1791
1792 /* GPEX */
1793 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1794 /* caller must free the struct ExpirePolicy */
1795 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1796                 struct ExpirePolicy **policy, char *cret)
1797 {
1798         static char *proto[] = {"room", "floor", "site", "mailboxes" };
1799         char cmd[256];
1800         register int ret;
1801
1802         if (!cret) return -2;
1803         if (!policy) return -2;
1804         if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1805         if (!*policy) return -1;
1806         if (which < 0 || which > 3) return -2;
1807         
1808         sprintf(cmd, "GPEX %s", proto[which]);
1809         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1810         if (ret / 100 == 2) {
1811                 policy[0]->expire_mode = extract_int(cret, 0);
1812                 policy[0]->expire_value = extract_int(cret, 1);
1813         }
1814         return ret;
1815 }
1816
1817
1818 /* SPEX */
1819 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1820 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1821 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1822                 struct ExpirePolicy *policy, char *cret)
1823 {
1824         char aaa[38];
1825         char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1826
1827         if (!cret) return -2;
1828         if (which < 0 || which > 3) return -2;
1829         if (!policy) return -2;
1830         if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1831         if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1832
1833         sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1834                         policy->expire_mode, policy->expire_value);
1835         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1836 }
1837
1838
1839 /* CONF GET */
1840 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1841 {
1842         size_t bytes;
1843
1844         if (!cret) return -2;
1845         if (!listing) return -2;
1846         if (*listing) return -2;
1847
1848         return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1849                         listing, &bytes, cret);
1850 }
1851
1852
1853 /* CONF SET */
1854 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1855 {
1856         if (!cret) return -2;
1857         if (!listing) return -2;
1858
1859         return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1860                         NULL, NULL, cret);
1861 }
1862
1863
1864 /* CONF GETSYS */
1865 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1866                 char **listing, char *cret)
1867 {
1868         register int ret;
1869         char *aaa;
1870         size_t bytes;
1871
1872         if (!cret) return -2;
1873         if (!mimetype) return -2;
1874         if (!listing) return -2;
1875         if (*listing) return -2;
1876
1877         aaa = malloc(strlen(mimetype) + 13);
1878         if (!aaa) return -1;
1879         sprintf(aaa, "CONF GETSYS|%s", mimetype);
1880         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1881                         listing, &bytes, cret);
1882     free(aaa);
1883     return ret;
1884 }
1885
1886
1887 /* CONF PUTSYS */
1888 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1889                const char *listing, char *cret)
1890 {
1891     register int ret;
1892         char *aaa;
1893
1894         if (!cret) return -2;
1895         if (!mimetype) return -2;
1896         if (!listing) return -2;
1897
1898         aaa = malloc(strlen(mimetype) + 13);
1899         if (!aaa) return -1;
1900         sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1901         ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1902                         NULL, NULL, cret);
1903     free(aaa);
1904     return ret;
1905 }
1906
1907
1908 /* GNET */
1909 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1910 {
1911         size_t bytes;
1912
1913         if (!cret) return -2;
1914         if (!listing) return -2;
1915         if (*listing) return -2;
1916
1917         return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1918                         listing, &bytes, cret);
1919 }
1920
1921
1922 /* SNET */
1923 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1924 {
1925         if (!cret) return -2;
1926         if (!listing) return -2;
1927
1928         return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1929                         NULL, NULL, cret);
1930 }
1931
1932
1933 /* REQT */
1934 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1935 {
1936         char aaa[16];
1937
1938         if (!cret) return -2;
1939         if (session < 0) return -2;
1940
1941         sprintf(aaa, "REQT %d", session);
1942         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1943 }
1944
1945
1946 /* SEEN */
1947 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1948 {
1949         char aaa[27];
1950
1951         if (!cret) return -2;
1952         if (msgnum < 0) return -2;
1953
1954         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1955         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1956 }
1957
1958
1959 /* STLS */
1960 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1961 {
1962         int a;
1963         int r;
1964         char buf[SIZ];
1965
1966 #ifdef HAVE_OPENSSL
1967         SSL *temp_ssl;
1968
1969         /* New SSL object */
1970         temp_ssl = SSL_new(ssl_ctx);
1971         if (!temp_ssl) {
1972                 error_printf("SSL_new failed: %s\n",
1973                                 ERR_reason_error_string(ERR_get_error()));
1974                 return -2;
1975         }
1976         /* Pointless flag waving */
1977 #if SSLEAY_VERSION_NUMBER >= 0x0922
1978         SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1979 #endif
1980
1981         if (!access(EGD_POOL, F_OK))
1982                 RAND_egd(EGD_POOL);
1983
1984         if (!RAND_status()) {
1985                 error_printf("PRNG not properly seeded\n");
1986                 return -2;
1987         }
1988
1989         /* Associate network connection with SSL object */
1990         if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1991                 error_printf("SSL_set_fd failed: %s\n",
1992                                 ERR_reason_error_string(ERR_get_error()));
1993                 return -2;
1994         }
1995
1996         if (status_hook != NULL)
1997                 status_hook("Requesting encryption...\r");
1998
1999         /* Ready to start SSL/TLS */
2000         /* Old code
2001         CtdlIPC_putline(ipc, "STLS");
2002         CtdlIPC_getline(ipc, buf);
2003         if (buf[0] != '2') {
2004                 error_printf("Server can't start TLS: %s\n", buf);
2005                 return 0;
2006         }
2007         */
2008         r = CtdlIPCGenericCommand(ipc,
2009                                   "STLS", NULL, 0, NULL, NULL, cret);
2010         if (r / 100 != 2) {
2011                 error_printf("Server can't start TLS: %s\n", buf);
2012                 endtls(temp_ssl);
2013                 return r;
2014         }
2015
2016         /* Do SSL/TLS handshake */
2017         if ((a = SSL_connect(temp_ssl)) < 1) {
2018                 error_printf("SSL_connect failed: %s\n",
2019                                 ERR_reason_error_string(ERR_get_error()));
2020                 endtls(temp_ssl);
2021                 return -2;
2022         }
2023         ipc->ssl = temp_ssl;
2024
2025         BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2026         {
2027                 int bits, alg_bits;
2028
2029                 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2030                 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2031                                 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2032                                 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2033                                 bits, alg_bits);
2034         }
2035         return r;
2036 #else
2037         return 0;
2038 #endif /* HAVE_OPENSSL */
2039 }
2040
2041
2042 #ifdef HAVE_OPENSSL
2043 static void endtls(SSL *ssl)
2044 {
2045         if (ssl) {
2046                 SSL_shutdown(ssl);
2047                 SSL_free(ssl);
2048         }
2049 }
2050 #endif
2051
2052
2053 /* QDIR */
2054 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2055 {
2056     register int ret;
2057         char *aaa;
2058
2059         if (!address) return -2;
2060         if (!cret) return -2;
2061
2062         aaa = (char *)malloc(strlen(address) + 6);
2063         if (!aaa) return -1;
2064
2065         sprintf(aaa, "QDIR %s", address);
2066         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2067     free(aaa);
2068     return ret;
2069 }
2070
2071
2072 /* IPGM */
2073 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2074 {
2075         char aaa[30];
2076
2077         if (!cret) return -2;
2078         sprintf(aaa, "IPGM %d", secret);
2079         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2080 }
2081
2082
2083 /* FSCK */
2084 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2085 {
2086         size_t size = 0;
2087
2088         if (!cret) return -2;
2089         if (!mret) return -2;
2090         if (*mret) return -2;
2091
2092         return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2093 }
2094
2095
2096 /*
2097  * Not implemented:
2098  * 
2099  * CHAT
2100  * ETLS
2101  * EXPI
2102  * GTLS
2103  * IGAB
2104  * MSG3
2105  * MSG4
2106  * NDOP
2107  * NETP
2108  * NUOP
2109  * SMTP
2110  */
2111
2112
2113 /* ************************************************************************** */
2114 /*             Stuff below this line is not for public consumption            */
2115 /* ************************************************************************** */
2116
2117
2118 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2119 {
2120         if (ipc->network_status_cb) ipc->network_status_cb(1);
2121 #ifdef THREADED_CLIENT
2122         pthread_mutex_lock(&(ipc->mutex));
2123 #endif
2124 }
2125
2126
2127 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2128 {
2129 #ifdef THREADED_CLIENT
2130         pthread_mutex_unlock(&(ipc->mutex));
2131 #endif
2132         if (ipc->network_status_cb) ipc->network_status_cb(0);
2133 }
2134
2135
2136 /* Read a listing from the server up to 000.  Append to dest if it exists */
2137 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2138 {
2139         size_t length = 0;
2140         size_t linelength;
2141         char *ret = NULL;
2142         char aaa[SIZ];
2143
2144         ret = dest;
2145         if (ret != NULL) {
2146                 length = strlen(ret);
2147         } else {
2148                 length = 0;
2149         }
2150
2151         while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2152                 linelength = strlen(aaa);
2153                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2154                 if (ret) {
2155                         strcpy(&ret[length], aaa);
2156                         length += linelength;
2157                         strcpy(&ret[length++], "\n");
2158                 }
2159         }
2160
2161         return(ret);
2162 }
2163
2164
2165 /* Send a listing to the server; generate the ending 000. */
2166 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2167 {
2168         char *text;
2169
2170         text = (char *)malloc(strlen(listing) + 6);
2171         if (text) {
2172                 strcpy(text, listing);
2173                 while (text[strlen(text) - 1] == '\n')
2174                         text[strlen(text) - 1] = '\0';
2175                 strcat(text, "\n000");
2176                 CtdlIPC_putline(ipc, text);
2177                 free(text);
2178                 text = NULL;
2179         } else {
2180                 /* Malloc failed but we are committed to send */
2181                 /* This may result in extra blanks at the bottom */
2182                 CtdlIPC_putline(ipc, text);
2183                 CtdlIPC_putline(ipc, "000");
2184         }
2185         return 0;
2186 }
2187
2188
2189 /* Partial read of file from server */
2190 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2191 {
2192         register size_t len = 0;
2193         char aaa[SIZ];
2194
2195         if (!buf) return 0;
2196         if (!cret) return 0;
2197         if (bytes < 1) return 0;
2198
2199         CtdlIPC_lock(ipc);
2200         sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2201         CtdlIPC_putline(ipc, aaa);
2202         CtdlIPC_getline(ipc, aaa);
2203         if (aaa[0] != '6')
2204                 strcpy(cret, &aaa[4]);
2205         else {
2206                 len = extract_long(&aaa[4], 0);
2207                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2208                 if (*buf) {
2209                         /* I know what I'm doing */
2210                         serv_read(ipc, ((char *)(*buf) + offset), len);
2211                 } else {
2212                         /* We have to read regardless */
2213                         serv_read(ipc, aaa, len);
2214                         len = 0;
2215                 }
2216         }
2217         CtdlIPC_unlock(ipc);
2218         return len;
2219 }
2220
2221
2222 /* CLOS */
2223 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2224 {
2225         register int ret;
2226
2227         if (!cret) return -2;
2228         if (!ipc->downloading) return -2;
2229
2230         ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2231         if (ret / 100 == 2)
2232                 ipc->downloading = 0;
2233         return ret;
2234 }
2235
2236
2237 /* MSGP */
2238 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2239         register int ret;
2240         char cmd[SIZ];
2241         
2242         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2243         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2244         return ret;
2245 }
2246
2247
2248
2249 /* READ */
2250 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2251                 void (*progress_gauge_callback)
2252                         (CtdlIPC*, unsigned long, unsigned long),
2253                char *cret)
2254 {
2255         register size_t len;
2256
2257         if (!cret) return -1;
2258         if (!buf) return -1;
2259         if (*buf) return -1;
2260         if (!ipc->downloading) return -1;
2261
2262         len = resume;
2263         if (progress_gauge_callback)
2264                 progress_gauge_callback(ipc, len, bytes);
2265         while (len < bytes) {
2266                 register size_t block;
2267
2268                 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2269                 if (block == 0) {
2270                         free(*buf);
2271                         return 0;
2272                 }
2273                 len += block;
2274                 if (progress_gauge_callback)
2275                         progress_gauge_callback(ipc, len, bytes);
2276         }
2277         return len;
2278 }
2279
2280 /* READ - pipelined */
2281 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2282                size_t resume,
2283                 void (*progress_gauge_callback)
2284                         (CtdlIPC*, unsigned long, unsigned long),
2285                char *cret)
2286 {
2287         register size_t len;
2288         register int calls;     /* How many calls in the pipeline */
2289         register int i;         /* iterator */
2290         char aaa[4096];
2291
2292         if (!cret) return -1;
2293         if (!buf) return -1;
2294         if (*buf) return -1;
2295         if (!ipc->downloading) return -1;
2296
2297         *buf = (void *)realloc(*buf, bytes - resume);
2298         if (!*buf) return -1;
2299
2300         len = 0;
2301         CtdlIPC_lock(ipc);
2302         if (progress_gauge_callback)
2303                 progress_gauge_callback(ipc, len, bytes);
2304
2305         /* How many calls will be in the pipeline? */
2306         calls = (bytes - resume) / 4096;
2307         if ((bytes - resume) % 4096) calls++;
2308
2309         /* Send all requests at once */
2310         for (i = 0; i < calls; i++) {
2311                 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2312                 CtdlIPC_putline(ipc, aaa);
2313         }
2314
2315         /* Receive all responses at once */
2316         for (i = 0; i < calls; i++) {
2317                 CtdlIPC_getline(ipc, aaa);
2318                 if (aaa[0] != '6')
2319                         strcpy(cret, &aaa[4]);
2320                 else {
2321                         len = extract_long(&aaa[4], 0);
2322                         /* I know what I'm doing */
2323                         serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2324                 }
2325                 if (progress_gauge_callback)
2326                         progress_gauge_callback(ipc, i * 4096 + len, bytes);
2327         }
2328         CtdlIPC_unlock(ipc);
2329         return len;
2330 }
2331
2332
2333 /* UCLS */
2334 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2335 {
2336         register int ret;
2337         char cmd[8];
2338
2339         if (!cret) return -1;
2340         if (!ipc->uploading) return -1;
2341
2342         sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2343         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2344         ipc->uploading = 0;
2345         return ret;
2346 }
2347
2348
2349 /* WRIT */
2350 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2351                 void (*progress_gauge_callback)
2352                         (CtdlIPC*, unsigned long, unsigned long),
2353                 char *cret)
2354 {
2355         register int ret = -1;
2356         register size_t offset = 0;
2357         size_t bytes;
2358         char aaa[SIZ];
2359         char buf[4096];
2360         FILE *fd;
2361         int ferr;
2362
2363         if (!cret) return -1;
2364         if (!path) return -1;
2365         if (!*path) return -1;
2366
2367         fd = fopen(path, "r");
2368         if (!fd) return -2;
2369
2370         fseek(fd, 0L, SEEK_END);
2371         bytes = ftell(fd);
2372         rewind(fd);
2373
2374         if (progress_gauge_callback)
2375                 progress_gauge_callback(ipc, 0, bytes);
2376
2377         while (offset < bytes) {
2378                 register size_t to_write;
2379
2380                 /* Read some data in */
2381                 to_write = fread(buf, 1, 4096, fd);
2382                 if (!to_write) {
2383                         if (feof(fd) || ferror(fd)) break;
2384                 }
2385                 sprintf(aaa, "WRIT %d", (int)to_write);
2386                 CtdlIPC_putline(ipc, aaa);
2387                 CtdlIPC_getline(ipc, aaa);
2388                 strcpy(cret, &aaa[4]);
2389                 ret = atoi(aaa);
2390                 if (aaa[0] == '7') {
2391                         to_write = extract_long(&aaa[4], 0);
2392                         
2393                         serv_write(ipc, buf, to_write);
2394                         offset += to_write;
2395                         if (progress_gauge_callback)
2396                                 progress_gauge_callback(ipc, offset, bytes);
2397                         /* Detect short reads and back up if needed */
2398                         /* offset will never be negative anyway */
2399                         fseek(fd, (signed)offset, SEEK_SET);
2400                 } else {
2401                         break;
2402                 }
2403         }
2404         if (progress_gauge_callback)
2405                 progress_gauge_callback(ipc, 1, 1);
2406         ferr = ferror(fd);
2407         fclose(fd);
2408         return (!ferr ? ret : -2);
2409 }
2410
2411
2412 /*
2413  * Generic command method.  This method should handle any server command
2414  * except for CHAT.  It takes the following arguments:
2415  *
2416  * ipc                  The server to speak with
2417  * command              Preformatted command to send to server
2418  * to_send              A text or binary file to send to server
2419  *                      (only sent if server requests it)
2420  * bytes_to_send        The number of bytes in to_send (required if
2421  *                      sending binary, optional if sending listing)
2422  * to_receive           Pointer to a NULL pointer, if the server
2423  *                      sends text or binary we will allocate memory
2424  *                      for the file and stuff it here
2425  * bytes_to_receive     If a file is received, we will store its
2426  *                      byte count here
2427  * proto_response       The protocol response.  Caller must provide
2428  *                      this buffer and ensure that it is at least
2429  *                      128 bytes in length.
2430  *
2431  * This function returns a number equal to the protocol response number,
2432  * -1 if an internal error occurred, -2 if caller provided bad values,
2433  * or 0 - the protocol response number if bad values were found during
2434  * the protocol exchange.
2435  * It stores the protocol response string (minus the number) in 
2436  * protocol_response as described above.  Some commands send additional
2437  * data in this string.
2438  */
2439 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2440                 const char *command, const char *to_send,
2441                 size_t bytes_to_send, char **to_receive, 
2442                 size_t *bytes_to_receive, char *proto_response)
2443 {
2444         char buf[SIZ];
2445         register int ret;
2446         int watch_ssl = 0;
2447
2448         if (!command) return -2;
2449         if (!proto_response) return -2;
2450
2451 #ifdef HAVE_OPENSSL
2452         if (ipc->ssl) watch_ssl = 1;
2453 #endif
2454
2455         CtdlIPC_lock(ipc);
2456         CtdlIPC_putline(ipc, command);
2457         while (1) {
2458                 CtdlIPC_getline(ipc, proto_response);
2459                 if (proto_response[3] == '*')
2460                         instant_msgs = 1;
2461                 ret = atoi(proto_response);
2462                 strcpy(proto_response, &proto_response[4]);
2463                 switch (ret / 100) {
2464                 default:                        /* Unknown, punt */
2465                 case 2:                         /* OK */
2466                 case 3:                         /* MORE_DATA */
2467                 case 5:                         /* ERROR */
2468                         /* Don't need to do anything */
2469                         break;
2470                 case 1:                         /* LISTING_FOLLOWS */
2471                         if (to_receive && !*to_receive && bytes_to_receive) {
2472                                 *to_receive = CtdlIPCReadListing(ipc, NULL);
2473                         } else { /* Drain */
2474                                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2475                                 ret = -ret;
2476                         }
2477                         break;
2478                 case 4:                         /* SEND_LISTING */
2479                         if (to_send) {
2480                                 CtdlIPCSendListing(ipc, to_send);
2481                         } else {
2482                                 /* No listing given, fake it */
2483                                 CtdlIPC_putline(ipc, "000");
2484                                 ret = -ret;
2485                         }
2486                         break;
2487                 case 6:                         /* BINARY_FOLLOWS */
2488                         if (to_receive && !*to_receive && bytes_to_receive) {
2489                                 *bytes_to_receive =
2490                                         extract_long(proto_response, 0);
2491                                 *to_receive = (char *)
2492                                         malloc((size_t)*bytes_to_receive);
2493                                 if (!*to_receive) {
2494                                         ret = -1;
2495                                 } else {
2496                                         serv_read(ipc, *to_receive,
2497                                                         *bytes_to_receive);
2498                                 }
2499                         } else {
2500                                 /* Drain */
2501                                 size_t drain;
2502
2503                                 drain = extract_long(proto_response, 0);
2504                                 while (drain > SIZ) {
2505                                         serv_read(ipc, buf, SIZ);
2506                                         drain -= SIZ;
2507                                 }
2508                                 serv_read(ipc, buf, drain);
2509                                 ret = -ret;
2510                         }
2511                         break;
2512                 case 7:                         /* SEND_BINARY */
2513                         if (to_send && bytes_to_send) {
2514                                 serv_write(ipc, to_send, bytes_to_send);
2515                         } else if (bytes_to_send) {
2516                                 /* Fake it, send nulls */
2517                                 size_t fake;
2518
2519                                 fake = bytes_to_send;
2520                                 memset(buf, '\0', SIZ);
2521                                 while (fake > SIZ) {
2522                                         serv_write(ipc, buf, SIZ);
2523                                         fake -= SIZ;
2524                                 }
2525                                 serv_write(ipc, buf, fake);
2526                                 ret = -ret;
2527                         } /* else who knows?  DANGER WILL ROBINSON */
2528                         break;
2529                 case 8:                         /* START_CHAT_MODE */
2530                         if (!strncasecmp(command, "CHAT", 4)) {
2531                                 /* Don't call chatmode with generic! */
2532                                 CtdlIPC_putline(ipc, "/quit");
2533                                 ret = -ret;
2534                         } else {
2535                                 /* In this mode we send then receive listing */
2536                                 if (to_send) {
2537                                         CtdlIPCSendListing(ipc, to_send);
2538                                 } else {
2539                                         /* No listing given, fake it */
2540                                         CtdlIPC_putline(ipc, "000");
2541                                         ret = -ret;
2542                                 }
2543                                 if (to_receive && !*to_receive
2544                                                 && bytes_to_receive) {
2545                                         *to_receive = CtdlIPCReadListing(ipc, NULL);
2546                                 } else { /* Drain */
2547                                         while (CtdlIPC_getline(ipc, buf),
2548                                                         strcmp(buf, "000")) ;
2549                                         ret = -ret;
2550                                 }
2551                         }
2552                         break;
2553                 case 9:                         /* ASYNC_MSG */
2554                         /* CtdlIPCDoAsync(ret, proto_response); */
2555                         free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
2556                         break;
2557                 }
2558                 if (ret / 100 != 9)
2559                         break;
2560         }
2561         CtdlIPC_unlock(ipc);
2562         return ret;
2563 }
2564
2565
2566 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2567 {
2568         struct hostent *phe;
2569         struct servent *pse;
2570         struct protoent *ppe;
2571         struct sockaddr_in sin;
2572         int s, type;
2573
2574         memset(&sin, 0, sizeof(sin));
2575         sin.sin_family = AF_INET;
2576
2577         pse = getservbyname(service, protocol);
2578         if (pse != NULL) {
2579                 sin.sin_port = pse->s_port;
2580         }
2581         else if (atoi(service) > 0) {
2582                 sin.sin_port = htons(atoi(service));
2583         }
2584         else {
2585                 sin.sin_port = htons(defaultPort);
2586         }
2587         phe = gethostbyname(host);
2588         if (phe) {
2589                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2590         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2591                 return -1;
2592         }
2593         if ((ppe = getprotobyname(protocol)) == 0) {
2594                 return -1;
2595         }
2596         if (!strcmp(protocol, "udp")) {
2597                 type = SOCK_DGRAM;
2598         } else {
2599                 type = SOCK_STREAM;
2600         }
2601
2602         s = socket(PF_INET, type, ppe->p_proto);
2603         if (s < 0) {
2604                 return -1;
2605         }
2606
2607         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2608                 close(s);
2609                 return -1;
2610         }
2611
2612         return (s);
2613 }
2614
2615 static int uds_connectsock(int *isLocal, char *sockpath)
2616 {
2617         struct sockaddr_un addr;
2618         int s;
2619
2620         memset(&addr, 0, sizeof(addr));
2621         addr.sun_family = AF_UNIX;
2622         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2623
2624         s = socket(AF_UNIX, SOCK_STREAM, 0);
2625         if (s < 0) {
2626                 return -1;
2627         }
2628
2629         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2630                 close(s);
2631                 return -1;
2632         }
2633
2634         *isLocal = 1;
2635         return s;
2636 }
2637
2638
2639 /*
2640  * input binary data from socket
2641  */
2642 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2643 {
2644         unsigned int len, rlen;
2645
2646 #if defined(HAVE_OPENSSL)
2647         if (ipc->ssl) {
2648                 serv_read_ssl(ipc, buf, bytes);
2649                 return;
2650         }
2651 #endif
2652         len = 0;
2653         while (len < bytes) {
2654                 rlen = read(ipc->sock, &buf[len], bytes - len);
2655                 if (rlen < 1) {
2656                         connection_died(ipc, 0);
2657                         return;
2658                 }
2659                 len += rlen;
2660         }
2661 }
2662
2663
2664 /*
2665  * send binary to server
2666  */
2667 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2668 {
2669         unsigned int bytes_written = 0;
2670         int retval;
2671
2672 #if defined(HAVE_OPENSSL)
2673         if (ipc->ssl) {
2674                 serv_write_ssl(ipc, buf, nbytes);
2675                 return;
2676         }
2677 #endif
2678         while (bytes_written < nbytes) {
2679                 retval = write(ipc->sock, &buf[bytes_written],
2680                                nbytes - bytes_written);
2681                 if (retval < 1) {
2682                         connection_died(ipc, 0);
2683                         return;
2684                 }
2685                 bytes_written += retval;
2686         }
2687 }
2688
2689
2690 #ifdef HAVE_OPENSSL
2691 /*
2692  * input binary data from encrypted connection
2693  */
2694 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2695 {
2696         int len, rlen;
2697         char junk[1];
2698
2699         len = 0;
2700         while (len < bytes) {
2701                 if (SSL_want_read(ipc->ssl)) {
2702                         if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2703                                 error_printf("SSL_write in serv_read:\n");
2704                                 ERR_print_errors_fp(stderr);
2705                         }
2706                 }
2707                 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2708                 if (rlen < 1) {
2709                         long errval;
2710
2711                         errval = SSL_get_error(ipc->ssl, rlen);
2712                         if (errval == SSL_ERROR_WANT_READ ||
2713                                         errval == SSL_ERROR_WANT_WRITE) {
2714                                 sleep(1);
2715                                 continue;
2716                         }
2717 /***
2718  Not sure why we'd want to handle these error codes any differently,
2719  but this definitely isn't the way to handle them.  Someone must have
2720  naively assumed that we could fall back to unencrypted communications,
2721  but all it does is just recursively blow the stack.
2722                         if (errval == SSL_ERROR_ZERO_RETURN ||
2723                                         errval == SSL_ERROR_SSL) {
2724                                 serv_read(ipc, &buf[len], bytes - len);
2725                                 return;
2726                         }
2727  ***/
2728                         error_printf("SSL_read in serv_read: %s\n",
2729                                         ERR_reason_error_string(ERR_peek_error()));
2730                         connection_died(ipc, 1);
2731                         return;
2732                 }
2733                 len += rlen;
2734         }
2735 }
2736
2737
2738 /*
2739  * send binary to server encrypted
2740  */
2741 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2742 {
2743         unsigned int bytes_written = 0;
2744         int retval;
2745         char junk[1];
2746
2747         while (bytes_written < nbytes) {
2748                 if (SSL_want_write(ipc->ssl)) {
2749                         if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2750                                 error_printf("SSL_read in serv_write:\n");
2751                                 ERR_print_errors_fp(stderr);
2752                         }
2753                 }
2754                 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2755                                 nbytes - bytes_written);
2756                 if (retval < 1) {
2757                         long errval;
2758
2759                         errval = SSL_get_error(ipc->ssl, retval);
2760                         if (errval == SSL_ERROR_WANT_READ ||
2761                                         errval == SSL_ERROR_WANT_WRITE) {
2762                                 sleep(1);
2763                                 continue;
2764                         }
2765                         if (errval == SSL_ERROR_ZERO_RETURN ||
2766                                         errval == SSL_ERROR_SSL) {
2767                                 serv_write(ipc, &buf[bytes_written],
2768                                                 nbytes - bytes_written);
2769                                 return;
2770                         }
2771                         error_printf("SSL_write in serv_write: %s\n",
2772                                         ERR_reason_error_string(ERR_peek_error()));
2773                         connection_died(ipc, 1);
2774                         return;
2775                 }
2776                 bytes_written += retval;
2777         }
2778 }
2779
2780
2781 #ifdef THREADED_CLIENT
2782 static void ssl_lock(int mode, int n, const char *file, int line)
2783 {
2784         if (mode & CRYPTO_LOCK)
2785                 pthread_mutex_lock(Critters[n]);
2786         else
2787                 pthread_mutex_unlock(Critters[n]);
2788 }
2789 #endif /* THREADED_CLIENT */
2790
2791
2792 static void CtdlIPC_init_OpenSSL(void)
2793 {
2794         int a;
2795         SSL_METHOD *ssl_method;
2796         DH *dh;
2797         
2798         /* already done init */
2799         if (ssl_ctx) {
2800                 return;
2801         }
2802
2803         /* Get started */
2804         a = 0;
2805         ssl_ctx = NULL;
2806         dh = NULL;
2807         SSL_load_error_strings();
2808         SSLeay_add_ssl_algorithms();
2809
2810         /* Set up the SSL context in which we will oeprate */
2811         ssl_method = SSLv23_client_method();
2812         ssl_ctx = SSL_CTX_new(ssl_method);
2813         if (!ssl_ctx) {
2814                 error_printf("SSL_CTX_new failed: %s\n",
2815                                 ERR_reason_error_string(ERR_get_error()));
2816                 return;
2817         }
2818         /* Any reasonable cipher we can get */
2819         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2820                 error_printf("No ciphers available for encryption\n");
2821                 return;
2822         }
2823         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2824         
2825         /* Load DH parameters into the context */
2826         dh = DH_new();
2827         if (!dh) {
2828                 error_printf("Can't allocate a DH object: %s\n",
2829                                 ERR_reason_error_string(ERR_get_error()));
2830                 return;
2831         }
2832         if (!(BN_hex2bn(&(dh->p), DH_P))) {
2833                 error_printf("Can't assign DH_P: %s\n",
2834                                 ERR_reason_error_string(ERR_get_error()));
2835                 DH_free(dh);
2836                 return;
2837         }
2838         if (!(BN_hex2bn(&(dh->g), DH_G))) {
2839                 error_printf("Can't assign DH_G: %s\n",
2840                                 ERR_reason_error_string(ERR_get_error()));
2841                 DH_free(dh);
2842                 return;
2843         }
2844         dh->length = DH_L;
2845         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2846         DH_free(dh);
2847
2848 #ifdef THREADED_CLIENT
2849         /* OpenSSL requires callbacks for threaded clients */
2850         CRYPTO_set_locking_callback(ssl_lock);
2851         CRYPTO_set_id_callback(id_callback);
2852
2853         /* OpenSSL requires us to do semaphores for threaded clients */
2854         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2855         if (!Critters) {
2856                 perror("malloc failed");
2857                 exit(1);
2858         } else {
2859                 for (a = 0; a < CRYPTO_num_locks(); a++) {
2860                         Critters[a] = malloc(sizeof (pthread_mutex_t));
2861                         if (!Critters[a]) {
2862                                 perror("malloc failed");
2863                                 exit(1);
2864                         }
2865                         pthread_mutex_init(Critters[a], NULL);
2866                 }
2867         }
2868 #endif /* THREADED_CLIENT */       
2869 }
2870
2871
2872
2873 #ifdef THREADED_CLIENT
2874 static unsigned long id_callback(void) {
2875         return (unsigned long)pthread_self();
2876 }
2877 #endif /* THREADED_CLIENT */
2878 #endif /* HAVE_OPENSSL */
2879
2880
2881 int
2882 ReadNetworkChunk(CtdlIPC* ipc)
2883 {
2884         fd_set read_fd;
2885         int tries;
2886         int ret = 0;
2887         int err = 0;
2888         struct timeval tv;
2889         size_t n;
2890
2891         tv.tv_sec = 1;
2892         tv.tv_usec = 1000;
2893         tries = 0;
2894         n = 0;
2895         while (1)
2896         {
2897                 errno=0;
2898                 FD_ZERO(&read_fd);
2899                 FD_SET(ipc->sock, &read_fd);
2900                 ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
2901                 
2902 //              fprintf(stderr, "\nselect failed: %d %d %s\n", ret,  err, strerror(err));
2903                 
2904                 if (ret > 0) {
2905                         
2906                         *(ipc->BufPtr) = '\0';
2907 //                      n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
2908                         n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1, 0);
2909                         if (n > 0) {
2910                                 ipc->BufPtr[n]='\0';
2911                                 ipc->BufUsed += n;
2912                                 return n;
2913                         }
2914                         else 
2915                                 return n;
2916                 }
2917                 else if (ret < 0) {
2918                         if (!(errno == EINTR || errno == EAGAIN))
2919                                 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2920                         return -1;
2921                 }/*
2922                 else {
2923                         tries ++;
2924                         if (tries >= 10)
2925                         n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
2926                         if (n > 0) {
2927                                 ipc->BufPtr[n]='\0';
2928                                 ipc->BufUsed += n;
2929                                 return n;
2930                         }
2931                         else {
2932                                 connection_died(ipc, 0);
2933                                 return -1;
2934                         }
2935                         }*/
2936         }
2937 }
2938
2939 /*
2940  * input string from socket - implemented in terms of serv_read()
2941  */
2942 #ifdef CHUNKED_READ
2943
2944 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2945 {
2946         int i, ntries;
2947         char *aptr, *bptr, *aeptr, *beptr;
2948
2949 //      error_printf("---\n");
2950
2951         beptr = buf + SIZ;
2952 #if defined(HAVE_OPENSSL)
2953         if (ipc->ssl) {
2954                 
2955                 /* Read one character at a time. */
2956                 for (i = 0;; i++) {
2957                         serv_read(ipc, &buf[i], 1);
2958                         if (buf[i] == '\n' || i == (SIZ-1))
2959                                 break;
2960                 }
2961                 
2962                 /* If we got a long line, discard characters until the newline. */
2963                 if (i == (SIZ-1))
2964                         while (buf[i] != '\n')
2965                                 serv_read(ipc, &buf[i], 1);
2966                 
2967                 /* Strip the trailing newline (and carriage return, if present) */
2968                 if (i>=0 && buf[i] == 10) buf[i--] = 0;
2969                 if (i>=0 && buf[i] == 13) buf[i--] = 0;
2970         }
2971         else
2972 #endif
2973         {
2974                 if (ipc->Buf == NULL)
2975                 {
2976                         ipc->BufSize = SIZ;
2977                         ipc->Buf = (char*) malloc(ipc->BufSize + 10);
2978                         *(ipc->Buf) = '\0';
2979                         ipc->BufPtr = ipc->Buf;
2980                 }
2981
2982                 ntries = 0;
2983 //              while ((ipc->BufUsed == 0)||(ntries++ > 10))
2984                 if (ipc->BufUsed == 0)
2985                         ReadNetworkChunk(ipc);
2986
2987 ////            if (ipc->BufUsed != 0) while (1)
2988                 bptr = buf;
2989
2990                 while (1)
2991                 {
2992                         aptr = ipc->BufPtr;
2993                         aeptr = ipc->Buf + ipc->BufSize;
2994                         while ((aptr < aeptr) && 
2995                                (bptr < beptr) &&
2996                                (*aptr != '\0') && 
2997                                (*aptr != '\n'))
2998                                 *(bptr++) = *(aptr++);
2999                         if ((*aptr == '\n') && (aptr < aeptr))
3000                         {
3001                                 /* Terminate it right, remove the line breaks */
3002                                 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3003                                         aptr ++;
3004                                 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3005                                         aptr ++;
3006                                 *(bptr++) = '\0';
3007 //                              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);
3008                                 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3009                                         *(--bptr) = '\0';
3010                                 
3011                                 /* is there more in the buffer we need to read later? */
3012                                 if (ipc->Buf + ipc->BufUsed > aptr)
3013                                 {
3014                                         ipc->BufPtr = aptr;
3015                                 }
3016                                 else
3017                                 {
3018                                         ipc->BufUsed = 0;
3019                                         ipc->BufPtr = ipc->Buf;
3020                                 }
3021 //                              error_printf("----bla6\n");
3022                                 return;
3023                                 
3024                         }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3025                         else if ((ipc->BufPtr != ipc->Buf) && 
3026                                  (ipc->BufUsed > (ipc->BufSize  - (ipc->BufSize / 4))))
3027                         {
3028                                 size_t NewBufSize = ipc->BufSize * 2;
3029                                 int delta = (ipc->BufPtr - ipc->Buf);
3030                                 char *NewBuf;
3031
3032                                 /* if the line would end after our buffer, we should use a bigger buffer. */
3033                                 NewBuf = (char *)malloc (NewBufSize + 10);
3034                                 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3035                                 free(ipc->Buf);
3036                                 ipc->Buf = ipc->BufPtr = NewBuf;
3037                                 ipc->BufUsed -= delta;
3038                                 ipc->BufSize = NewBufSize;
3039                         }
3040                         if (ReadNetworkChunk(ipc) <0)
3041                         {
3042 //                              error_printf("----bla\n");
3043                                 return;
3044                         }
3045                 }
3046 ///             error_printf("----bl45761%s\nipc->BufUsed");
3047         }
3048 //      error_printf("----bla1\n");
3049 }
3050
3051 #else   /* CHUNKED_READ */
3052
3053 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3054 {
3055         int i;
3056
3057         /* Read one character at a time. */
3058         for (i = 0;; i++) {
3059                 serv_read(ipc, &buf[i], 1);
3060                 if (buf[i] == '\n' || i == (SIZ-1))
3061                         break;
3062         }
3063
3064         /* If we got a long line, discard characters until the newline. */
3065         if (i == (SIZ-1))
3066                 while (buf[i] != '\n')
3067                         serv_read(ipc, &buf[i], 1);
3068
3069         /* Strip the trailing newline (and carriage return, if present) */
3070         if (i>=0 && buf[i] == 10) buf[i--] = 0;
3071         if (i>=0 && buf[i] == 13) buf[i--] = 0;
3072 }
3073
3074
3075 #endif  /* CHUNKED_READ */
3076
3077
3078 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3079 {
3080         CtdlIPC_getline(ipc, buf);
3081 }
3082
3083 /*
3084  * send line to server - implemented in terms of serv_write()
3085  */
3086 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3087 {
3088         char *cmd = NULL;
3089         int len;
3090
3091         len = strlen(buf);
3092         cmd = malloc(len + 2);
3093         if (!cmd) {
3094                 /* This requires no extra memory */
3095                 serv_write(ipc, buf, len);
3096                 serv_write(ipc, "\n", 1);
3097         } else {
3098                 /* This is network-optimized */
3099                 strncpy(cmd, buf, len);
3100                 strcpy(cmd + len, "\n");
3101                 serv_write(ipc, cmd, len + 1);
3102                 free(cmd);
3103         }
3104
3105         ipc->last_command_sent = time(NULL);
3106 }
3107
3108 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3109 {
3110         CtdlIPC_putline(ipc, buf);
3111 }
3112
3113
3114 /*
3115  * attach to server
3116  */
3117 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3118 {
3119         int a;
3120         char cithost[SIZ];
3121         char citport[SIZ];
3122         char sockpath[SIZ];
3123         CtdlIPC* ipc;
3124
3125         ipc = ialloc(CtdlIPC);
3126         if (!ipc) {
3127                 return 0;
3128         }
3129 #if defined(HAVE_OPENSSL)
3130         ipc->ssl = NULL;
3131         CtdlIPC_init_OpenSSL();
3132 #endif
3133 #if defined(HAVE_PTHREAD_H)
3134         pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3135 #endif
3136         ipc->sock = -1;                 /* Not connected */
3137         ipc->isLocal = 0;               /* Not local, of course! */
3138         ipc->downloading = 0;
3139         ipc->uploading = 0;
3140         ipc->last_command_sent = 0L;
3141         ipc->network_status_cb = NULL;
3142         ipc->Buf = NULL;
3143         ipc->BufUsed = 0;
3144         ipc->BufPtr = NULL;
3145
3146         strcpy(cithost, DEFAULT_HOST);  /* default host */
3147         strcpy(citport, DEFAULT_PORT);  /* default port */
3148
3149         /* Allow caller to supply our values (Windows) */
3150         if (hostbuf && strlen(hostbuf) > 0)
3151                 strcpy(cithost, hostbuf);
3152         if (portbuf && strlen(portbuf) > 0)
3153                 strcpy(citport, portbuf);
3154
3155         /* Read host/port from command line if present */
3156         for (a = 0; a < argc; ++a) {
3157                 if (a == 0) {
3158                         /* do nothing */
3159                 } else if (a == 1) {
3160                         strcpy(cithost, argv[a]);
3161                 } else if (a == 2) {
3162                         strcpy(citport, argv[a]);
3163                 } else {
3164                         error_printf("%s: usage: ",argv[0]);
3165                         error_printf("%s [host] [port] ",argv[0]);
3166                         ifree(ipc);
3167                         errno = EINVAL;
3168                         return 0;
3169                 }
3170         }
3171
3172         if ((!strcmp(cithost, "localhost"))
3173            || (!strcmp(cithost, "127.0.0.1"))) {
3174                 ipc->isLocal = 1;
3175         }
3176
3177         /* If we're using a unix domain socket we can do a bunch of stuff */
3178         if (!strcmp(cithost, UDS)) {
3179                 if (!strcasecmp(citport, DEFAULT_PORT)) {
3180                         snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3181                 }
3182                 else {
3183                         snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3184                 }
3185                 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3186                 if (ipc->sock == -1) {
3187                         ifree(ipc);
3188                         return 0;
3189                 }
3190                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3191                 if (portbuf != NULL) strcpy(portbuf, sockpath);
3192                 return ipc;
3193         }
3194
3195         ipc->sock = connectsock(cithost, citport, "tcp", 504);
3196         if (ipc->sock == -1) {
3197                 ifree(ipc);
3198                 return 0;
3199         }
3200         if (hostbuf != NULL) strcpy(hostbuf, cithost);
3201         if (portbuf != NULL) strcpy(portbuf, citport);
3202         return ipc;
3203 }
3204
3205
3206 /*
3207  * Disconnect and delete the IPC class (destructor)
3208  */
3209 void CtdlIPC_delete(CtdlIPC* ipc)
3210 {
3211 #ifdef HAVE_OPENSSL
3212         if (ipc->ssl) {
3213                 SSL_shutdown(ipc->ssl);
3214                 SSL_free(ipc->ssl);
3215                 ipc->ssl = NULL;
3216         }
3217 #endif
3218         if (ipc->sock > -1) {
3219                 shutdown(ipc->sock, 2); /* Close it up */
3220                 ipc->sock = -1;
3221         }
3222         if (ipc->Buf != NULL)
3223                 free (ipc->Buf);
3224         ipc->Buf = NULL;
3225         ipc->BufPtr = NULL;
3226         ifree(ipc);
3227 }
3228
3229
3230 /*
3231  * Disconnect and delete the IPC class (destructor)
3232  * Also NULLs out the pointer
3233  */
3234 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3235 {
3236         CtdlIPC_delete(*pipc);
3237         *pipc = NULL;
3238 }
3239
3240
3241 /*
3242  * return the file descriptor of the server socket so we can select() on it.
3243  *
3244  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3245  * rewritten...
3246  */
3247 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3248 {
3249         return ipc->sock;
3250 }
3251
3252
3253 /*
3254  * return one character
3255  *
3256  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3257  * rewritten...
3258  */
3259 char CtdlIPC_get(CtdlIPC* ipc)
3260 {
3261         char buf[2];
3262         char ch;
3263
3264         serv_read(ipc, buf, 1);
3265         ch = (int) buf[0];
3266
3267         return (ch);
3268 }