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