df52ea719473c0e96f6a81f997b8086a78a1646e
[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, int *subject_required,  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         if ((flag == 0) && (subject_required != NULL)) {
855                 /* Is the server strongly recommending that the user enter a message subject? */
856                 if ((cret[3] != '\0') && (cret[4] != '\0')) {
857                         *subject_required = extract_int(&cret[4], 1);
858                 }
859
860                 
861         }
862         return ret;
863 }
864
865
866 /* RINF */
867 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
868 {
869         size_t bytes;
870
871         if (!cret) return -2;
872         if (!iret) return -2;
873         if (*iret) return -2;
874
875         return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
876 }
877
878
879 /* DELE */
880 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
881 {
882         char aaa[16];
883
884         if (!cret) return -2;
885         if (!msgnum) return -2;
886
887         sprintf(aaa, "DELE %ld", msgnum);
888         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
889 }
890
891
892 /* MOVE */
893 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
894 {
895         register int ret;
896         char *aaa;
897
898         if (!cret) return -2;
899         if (!destroom) return -2;
900         if (!msgnum) return -2;
901
902         aaa = (char *)malloc(strlen(destroom) + 28);
903         if (!aaa) return -1;
904
905         sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
906         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
907         free(aaa);
908         return ret;
909 }
910
911
912 /* KILL */
913 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
914 {
915         char aaa[16];
916
917         if (!cret) return -2;
918
919         sprintf(aaa, "KILL %d", for_real);
920         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
921 }
922
923
924 /* CRE8 */
925 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
926                 const char *password, int floor, char *cret)
927 {
928         register int ret;
929         char *aaa;
930
931         if (!cret) return -2;
932         if (!roomname) return -2;
933
934         if (password) {
935                 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
936                 if (!aaa) return -1;
937                 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
938                                 password, floor);
939         } else {
940                 aaa = (char *)malloc(strlen(roomname) + 40);
941                 if (!aaa) return -1;
942                 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
943                                 floor);
944         }
945         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
946         free(aaa);
947         return ret;
948 }
949
950
951 /* FORG */
952 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
953 {
954         if (!cret) return -2;
955
956         return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
957 }
958
959
960 /* MESG */
961 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
962 {
963         register int ret;
964         char *aaa;
965         size_t bytes;
966
967         if (!cret) return -2;
968         if (!mret) return -2;
969         if (*mret) return -2;
970         if (!message) return -2;
971
972         aaa = (char *)malloc(strlen(message) + 6);
973         if (!aaa) return -1;
974
975         sprintf(aaa, "MESG %s", message);
976         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
977         free(aaa);
978         return ret;
979 }
980
981
982 /* GNUR */
983 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
984 {
985         if (!cret) return -2;
986
987         return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
988 }
989
990
991 /* GREG */
992 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
993 {
994         register int ret;
995         char *aaa;
996         size_t bytes;
997
998         if (!cret) return -2;
999         if (!rret) return -2;
1000         if (*rret) return -2;
1001
1002         if (username)
1003                 aaa = (char *)malloc(strlen(username) + 6);
1004         else
1005                 aaa = (char *)malloc(12);
1006         if (!aaa) return -1;
1007
1008         if (username)
1009                 sprintf(aaa, "GREG %s", username);
1010         else
1011                 sprintf(aaa, "GREG _SELF_");
1012         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1013         free(aaa);
1014         return ret;
1015 }
1016
1017
1018 /* VALI */
1019 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1020 {
1021         register int ret;
1022         char *aaa;
1023
1024         if (!cret) return -2;
1025         if (!username) return -2;
1026         if (axlevel < 0 || axlevel > 7) return -2;
1027
1028         aaa = (char *)malloc(strlen(username) + 17);
1029         if (!aaa) return -1;
1030
1031         sprintf(aaa, "VALI %s|%d", username, axlevel);
1032         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1033         free(aaa);
1034         return ret;
1035 }
1036
1037
1038 /* EINF */
1039 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1040 {
1041         char aaa[16];
1042
1043         if (!cret) return -1;
1044         if (!info) return -1;
1045
1046         sprintf(aaa, "EINF %d", for_real);
1047         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1048 }
1049
1050
1051 /* LIST */
1052 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1053 {
1054         size_t bytes;
1055         char *cmd;
1056         int ret;
1057
1058         if (!cret) return -1;
1059         if (!listing) return -1;
1060         if (*listing) return -1;
1061         if (!searchstring) return -1;
1062
1063         cmd = malloc(strlen(searchstring) + 10);
1064         sprintf(cmd, "LIST %s", searchstring);
1065
1066         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1067         free(cmd);
1068         return(ret);
1069 }
1070
1071
1072 /* REGI */
1073 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1074 {
1075         if (!cret) return -1;
1076         if (!info) return -1;
1077
1078         return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1079                         NULL, NULL, cret);
1080 }
1081
1082
1083 /* CHEK */
1084 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1085 {
1086         register int ret;
1087
1088         if (!cret) return -1;
1089         if (!chek) return -1;
1090
1091         ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1092         if (ret / 100 == 2) {
1093                 chek->newmail = extract_long(cret, 0);
1094                 chek->needregis = extract_int(cret, 1);
1095                 chek->needvalid = extract_int(cret, 2);
1096         }
1097         return ret;
1098 }
1099
1100
1101 /* DELF */
1102 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1103 {
1104         register int ret;
1105         char *aaa;
1106
1107         if (!cret) return -2;
1108         if (!filename) return -2;
1109         
1110         aaa = (char *)malloc(strlen(filename) + 6);
1111         if (!aaa) return -1;
1112
1113         sprintf(aaa, "DELF %s", filename);
1114         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1115         free(aaa);
1116         return ret;
1117 }
1118
1119
1120 /* MOVF */
1121 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1122 {
1123         register int ret;
1124         char *aaa;
1125
1126         if (!cret) return -2;
1127         if (!filename) return -2;
1128         if (!destroom) return -2;
1129
1130         aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1131         if (!aaa) return -1;
1132
1133         sprintf(aaa, "MOVF %s|%s", filename, destroom);
1134         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1135         free(aaa);
1136         return ret;
1137 }
1138
1139
1140 /* NETF */
1141 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1142 {
1143         register int ret;
1144         char *aaa;
1145
1146         if (!cret) return -2;
1147         if (!filename) return -2;
1148         if (!destnode) return -2;
1149
1150         aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1151         if (!aaa) return -1;
1152
1153         sprintf(aaa, "NETF %s|%s", filename, destnode);
1154         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1155         free(aaa);
1156         return ret;
1157 }
1158
1159
1160 /* RWHO */
1161 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1162 {
1163         register int ret;
1164         size_t bytes;
1165
1166         if (!cret) return -1;
1167         if (!listing) return -1;
1168         if (*listing) return -1;
1169
1170         *stamp = CtdlIPCServerTime(ipc, cret);
1171         if (!*stamp)
1172                 *stamp = time(NULL);
1173         ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1174         return ret;
1175 }
1176
1177
1178 /* OPEN */
1179 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1180                 size_t resume,
1181                 void (*progress_gauge_callback)
1182                         (CtdlIPC*, unsigned long, unsigned long),
1183                 char *cret)
1184 {
1185         register int ret;
1186         size_t bytes;
1187         time_t last_mod;
1188         char mimetype[SIZ];
1189         char *aaa;
1190
1191         if (!cret) return -2;
1192         if (!filename) return -2;
1193         if (!buf) return -2;
1194         if (*buf) return -2;
1195         if (ipc->downloading) return -2;
1196
1197         aaa = (char *)malloc(strlen(filename) + 6);
1198         if (!aaa) return -1;
1199
1200         sprintf(aaa, "OPEN %s", filename);
1201         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1202         free(aaa);
1203         if (ret / 100 == 2) {
1204                 ipc->downloading = 1;
1205                 bytes = extract_long(cret, 0);
1206                 last_mod = extract_int(cret, 1);
1207                 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1208
1209                 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1210                                         progress_gauge_callback, cret);
1211                 /*
1212                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1213                                         progress_gauge_callback, cret);
1214                 */
1215
1216                 ret = CtdlIPCEndDownload(ipc, cret);
1217                 if (ret / 100 == 2)
1218                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1219                                         filename, mimetype);
1220         }
1221         return ret;
1222 }
1223
1224
1225 /* OPNA */
1226 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1227                 void **buf,
1228                 void (*progress_gauge_callback)
1229                         (CtdlIPC*, unsigned long, unsigned long),
1230                 char *cret)
1231 {
1232         register int ret;
1233         size_t bytes;
1234         time_t last_mod;
1235         char filename[SIZ];
1236         char mimetype[SIZ];
1237         char aaa[SIZ];
1238
1239         if (!cret) return -2;
1240         if (!buf) return -2;
1241         if (*buf) return -2;
1242         if (!part) return -2;
1243         if (!msgnum) return -2;
1244         if (ipc->downloading) return -2;
1245
1246         sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1247         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1248         if (ret / 100 == 2) {
1249                 ipc->downloading = 1;
1250                 bytes = extract_long(cret, 0);
1251                 last_mod = extract_int(cret, 1);
1252                 extract_token(filename, cret, 2, '|', sizeof filename);
1253                 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1254                 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1255                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1256                 ret = CtdlIPCEndDownload(ipc, cret);
1257                 if (ret / 100 == 2)
1258                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1259                                         filename, mimetype);
1260         }
1261         return ret;
1262 }
1263
1264
1265 /* OIMG */
1266 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1267                 void (*progress_gauge_callback)
1268                         (CtdlIPC*, unsigned long, unsigned long),
1269                 char *cret)
1270 {
1271         register int ret;
1272         size_t bytes;
1273         time_t last_mod;
1274         char mimetype[SIZ];
1275         char *aaa;
1276
1277         if (!cret) return -1;
1278         if (!buf) return -1;
1279         if (*buf) return -1;
1280         if (!filename) return -1;
1281         if (ipc->downloading) return -1;
1282
1283         aaa = (char *)malloc(strlen(filename) + 6);
1284         if (!aaa) return -1;
1285
1286         sprintf(aaa, "OIMG %s", filename);
1287         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1288         free(aaa);
1289         if (ret / 100 == 2) {
1290                 ipc->downloading = 1;
1291                 bytes = extract_long(cret, 0);
1292                 last_mod = extract_int(cret, 1);
1293                 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1294 /*              ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1295                 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1296                 ret = CtdlIPCEndDownload(ipc, cret);
1297                 if (ret / 100 == 2)
1298                         sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1299                                         filename, mimetype);
1300         }
1301         return ret;
1302 }
1303
1304
1305 /* UOPN */
1306 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1307                 const char *path,
1308                 void (*progress_gauge_callback)
1309                         (CtdlIPC*, unsigned long, unsigned long),
1310                 char *cret)
1311 {
1312         register int ret;
1313         char *aaa;
1314
1315         if (!cret) return -1;
1316         if (!save_as) return -1;
1317         if (!comment) return -1;
1318         if (!path) return -1;
1319         if (!*path) return -1;
1320         if (ipc->uploading) return -1;
1321
1322         aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1323         if (!aaa) return -1;
1324
1325         sprintf(aaa, "UOPN %s|%s", save_as, comment);
1326         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1327         free(aaa);
1328         if (ret / 100 == 2) {
1329                 ipc->uploading = 1;
1330                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1331                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1332                 ipc->uploading = 0;
1333         }
1334         return ret;
1335 }
1336
1337
1338 /* UIMG */
1339 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1340                 const char *save_as,
1341                 void (*progress_gauge_callback)
1342                         (CtdlIPC*, unsigned long, unsigned long),
1343                 char *cret)
1344 {
1345         register int ret;
1346         char *aaa;
1347
1348         if (!cret) return -1;
1349         if (!save_as) return -1;
1350         if (!path && for_real) return -1;
1351         if (!*path && for_real) return -1;
1352         if (ipc->uploading) return -1;
1353
1354         aaa = (char *)malloc(strlen(save_as) + 17);
1355         if (!aaa) return -1;
1356
1357         sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1358         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1359         free(aaa);
1360         if (ret / 100 == 2 && for_real) {
1361                 ipc->uploading = 1;
1362                 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1363                 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1364                 ipc->uploading = 0;
1365         }
1366         return ret;
1367 }
1368
1369
1370 /* QUSR */
1371 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1372 {
1373         register int ret;
1374         char *aaa;
1375
1376         if (!cret) return -2;
1377         if (!username) return -2;
1378
1379         aaa = (char *)malloc(strlen(username) + 6);
1380         if (!aaa) return -1;
1381
1382         sprintf(aaa, "QUSR %s", username);
1383         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1384         free(aaa);
1385         return ret;
1386 }
1387
1388
1389 /* LFLR */
1390 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1391 {
1392         size_t bytes;
1393
1394         if (!cret) return -2;
1395         if (!listing) return -2;
1396         if (*listing) return -2;
1397
1398         return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1399 }
1400
1401
1402 /* CFLR */
1403 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1404 {
1405         register int ret;
1406         char aaa[SIZ];
1407
1408         if (!cret) return -2;
1409         if (!name) return -2;
1410
1411         sprintf(aaa, "CFLR %s|%d", name, for_real);
1412         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1413         return ret;
1414 }
1415
1416
1417 /* KFLR */
1418 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1419 {
1420         char aaa[SIZ];
1421
1422         if (!cret) return -1;
1423         if (floornum < 0) return -1;
1424
1425         sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1426         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1427 }
1428
1429
1430 /* EFLR */
1431 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1432 {
1433         register int ret;
1434         char aaa[SIZ];
1435
1436         if (!cret) return -2;
1437         if (!floorname) return -2;
1438         if (floornum < 0) return -2;
1439
1440         sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1441         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1442         return ret;
1443 }
1444
1445
1446 /*
1447  * IDEN 
1448  *
1449  * You only need to fill out hostname, the defaults will be used if any of the
1450  * other fields are not set properly.
1451  */
1452 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1453                 int revision, const char *software_name, const char *hostname,
1454                 char *cret)
1455 {
1456         register int ret;
1457         char *aaa;
1458
1459         if (developerid < 0 || clientid < 0 || revision < 0 ||
1460             !software_name) {
1461                 developerid = 8;
1462                 clientid = 0;
1463                 revision = REV_LEVEL - 600;
1464                 software_name = "Citadel (libcitadel)";
1465         }
1466         if (!hostname) return -2;
1467
1468         aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1469         if (!aaa) return -1;
1470
1471         sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1472                         revision, software_name, hostname);
1473         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1474         free(aaa);
1475         return ret;
1476 }
1477
1478
1479 /* SEXP */
1480 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1481                 char *cret)
1482 {
1483         register int ret;
1484         char *aaa;
1485
1486         if (!cret) return -2;
1487         if (!username) return -2;
1488
1489         aaa = (char *)malloc(strlen(username) + 8);
1490         if (!aaa) return -1;
1491
1492         if (text) {
1493                 sprintf(aaa, "SEXP %s|-", username);
1494                 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1495                                 NULL, NULL, cret);
1496         } else {
1497                 sprintf(aaa, "SEXP %s||", username);
1498                 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1499         }
1500         free(aaa);
1501         return ret;
1502 }
1503
1504
1505 /* GEXP */
1506 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1507 {
1508         size_t bytes;
1509
1510         if (!cret) return -2;
1511         if (!listing) return -2;
1512         if (*listing) return -2;
1513
1514         return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1515 }
1516
1517
1518 /* DEXP */
1519 /* mode is 0 = enable, 1 = disable, 2 = status */
1520 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1521 {
1522         char aaa[16];
1523
1524         if (!cret) return -2;
1525
1526         sprintf(aaa, "DEXP %d", mode);
1527         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1528 }
1529
1530
1531 /* EBIO */
1532 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1533 {
1534         if (!cret) return -2;
1535         if (!bio) return -2;
1536
1537         return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1538                         NULL, NULL, cret);
1539 }
1540
1541
1542 /* RBIO */
1543 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1544 {
1545         register int ret;
1546         size_t bytes;
1547         char *aaa;
1548
1549         if (!cret) return -2;
1550         if (!username) return -2;
1551         if (!listing) return -2;
1552         if (*listing) return -2;
1553
1554         aaa = (char *)malloc(strlen(username) + 6);
1555         if (!aaa) return -1;
1556
1557         sprintf(aaa, "RBIO %s", username);
1558         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1559         free(aaa);
1560         return ret;
1561 }
1562
1563
1564 /* LBIO */
1565 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1566 {
1567         size_t bytes;
1568
1569         if (!cret) return -2;
1570         if (!listing) return -2;
1571         if (*listing) return -2;
1572
1573         return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1574 }
1575
1576
1577 /* STEL */
1578 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1579 {
1580         char aaa[16];
1581
1582         if (!cret) return -1;
1583
1584         sprintf(aaa, "STEL %d", mode ? 1 : 0);
1585         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1586 }
1587
1588
1589 /* TERM */
1590 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1591 {
1592         char aaa[16];
1593
1594         if (!cret) return -1;
1595
1596         sprintf(aaa, "TERM %d", sid);
1597         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1598 }
1599
1600
1601 /* DOWN */
1602 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1603 {
1604         if (!cret) return -1;
1605
1606         return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1607 }
1608
1609
1610 /* SCDN */
1611 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1612 {
1613         char aaa[16];
1614
1615         if (!cret) return -1;
1616
1617         sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1618         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1619 }
1620
1621
1622 /* EMSG */
1623 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1624                 char *cret)
1625 {
1626         register int ret;
1627         char *aaa;
1628
1629         if (!cret) return -2;
1630         if (!text) return -2;
1631         if (!filename) return -2;
1632
1633         aaa = (char *)malloc(strlen(filename) + 6);
1634         if (!aaa) return -1;
1635
1636         sprintf(aaa, "EMSG %s", filename);
1637         ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1638         free(aaa);
1639         return ret;
1640 }
1641
1642
1643 /* HCHG */
1644 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1645 {
1646         register int ret;
1647         char *aaa;
1648
1649         if (!cret) return -2;
1650         if (!hostname) return -2;
1651
1652         aaa = (char *)malloc(strlen(hostname) + 6);
1653         if (!aaa) return -1;
1654
1655         sprintf(aaa, "HCHG %s", hostname);
1656         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1657         free(aaa);
1658         return ret;
1659 }
1660
1661
1662 /* RCHG */
1663 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1664 {
1665         register int ret;
1666         char *aaa;
1667
1668         if (!cret) return -2;
1669         if (!roomname) return -2;
1670
1671         aaa = (char *)malloc(strlen(roomname) + 6);
1672         if (!aaa) return -1;
1673
1674         sprintf(aaa, "RCHG %s", roomname);
1675         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1676         free(aaa);
1677         return ret;
1678 }
1679
1680
1681 /* UCHG */
1682 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1683 {
1684         register int ret;
1685         char *aaa;
1686
1687         if (!cret) return -2;
1688         if (!username) return -2;
1689
1690         aaa = (char *)malloc(strlen(username) + 6);
1691         if (!aaa) return -1;
1692
1693         sprintf(aaa, "UCHG %s", username);
1694         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1695         free(aaa);
1696         return ret;
1697 }
1698
1699
1700 /* TIME */
1701 /* This function returns the actual server time reported, or 0 if error */
1702 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1703 {
1704         register time_t tret;
1705         register int ret;
1706
1707         ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1708         if (ret / 100 == 2) {
1709                 tret = extract_long(cret, 0);
1710         } else {
1711                 tret = 0L;
1712         }
1713         return tret;
1714 }
1715
1716
1717 /* AGUP */
1718 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1719                                  struct ctdluser **uret, char *cret)
1720 {
1721         register int ret;
1722         char aaa[SIZ];
1723
1724         if (!cret) return -2;
1725         if (!uret) return -2;
1726         if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1727         if (!*uret) return -1;
1728
1729         sprintf(aaa, "AGUP %s", who);
1730         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1731
1732         if (ret / 100 == 2) {
1733                 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1734                 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1735                 uret[0]->flags = extract_int(cret, 2);
1736                 uret[0]->timescalled = extract_long(cret, 3);
1737                 uret[0]->posted = extract_long(cret, 4);
1738                 uret[0]->axlevel = extract_int(cret, 5);
1739                 uret[0]->usernum = extract_long(cret, 6);
1740                 uret[0]->lastcall = extract_long(cret, 7);
1741                 uret[0]->USuserpurge = extract_int(cret, 8);
1742         }
1743         return ret;
1744 }
1745
1746
1747 /* ASUP */
1748 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1749 {
1750         register int ret;
1751         char *aaa;
1752
1753         if (!cret) return -2;
1754         if (!uret) return -2;
1755
1756         aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1757         if (!aaa) return -1;
1758
1759         sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1760                         uret->fullname, uret->password, uret->flags,
1761                         uret->timescalled, uret->posted, uret->axlevel,
1762                         uret->usernum, uret->lastcall, uret->USuserpurge);
1763         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1764         free(aaa);
1765         return ret;
1766 }
1767
1768
1769 /* GPEX */
1770 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1771 /* caller must free the struct ExpirePolicy */
1772 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1773                 struct ExpirePolicy **policy, char *cret)
1774 {
1775         static char *proto[] = {"room", "floor", "site", "mailboxes" };
1776         char cmd[256];
1777         register int ret;
1778
1779         if (!cret) return -2;
1780         if (!policy) return -2;
1781         if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1782         if (!*policy) return -1;
1783         if (which < 0 || which > 3) return -2;
1784         
1785         sprintf(cmd, "GPEX %s", proto[which]);
1786         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1787         if (ret / 100 == 2) {
1788                 policy[0]->expire_mode = extract_int(cret, 0);
1789                 policy[0]->expire_value = extract_int(cret, 1);
1790         }
1791         return ret;
1792 }
1793
1794
1795 /* SPEX */
1796 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1797 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1798 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1799                 struct ExpirePolicy *policy, char *cret)
1800 {
1801         char aaa[38];
1802         char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1803
1804         if (!cret) return -2;
1805         if (which < 0 || which > 3) return -2;
1806         if (!policy) return -2;
1807         if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1808         if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1809
1810         sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1811                         policy->expire_mode, policy->expire_value);
1812         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1813 }
1814
1815
1816 /* CONF GET */
1817 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1818 {
1819         size_t bytes;
1820
1821         if (!cret) return -2;
1822         if (!listing) return -2;
1823         if (*listing) return -2;
1824
1825         return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1826                         listing, &bytes, cret);
1827 }
1828
1829
1830 /* CONF SET */
1831 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1832 {
1833         if (!cret) return -2;
1834         if (!listing) return -2;
1835
1836         return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1837                         NULL, NULL, cret);
1838 }
1839
1840
1841 /* CONF GETSYS */
1842 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1843                 char **listing, char *cret)
1844 {
1845         register int ret;
1846         char *aaa;
1847         size_t bytes;
1848
1849         if (!cret) return -2;
1850         if (!mimetype) return -2;
1851         if (!listing) return -2;
1852         if (*listing) return -2;
1853
1854         aaa = malloc(strlen(mimetype) + 13);
1855         if (!aaa) return -1;
1856         sprintf(aaa, "CONF GETSYS|%s", mimetype);
1857         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1858                         listing, &bytes, cret);
1859     free(aaa);
1860     return ret;
1861 }
1862
1863
1864 /* CONF PUTSYS */
1865 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1866                const char *listing, char *cret)
1867 {
1868     register int ret;
1869         char *aaa;
1870
1871         if (!cret) return -2;
1872         if (!mimetype) return -2;
1873         if (!listing) return -2;
1874
1875         aaa = malloc(strlen(mimetype) + 13);
1876         if (!aaa) return -1;
1877         sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1878         ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1879                         NULL, NULL, cret);
1880     free(aaa);
1881     return ret;
1882 }
1883
1884
1885 /* GNET */
1886 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1887 {
1888         size_t bytes;
1889
1890         if (!cret) return -2;
1891         if (!listing) return -2;
1892         if (*listing) return -2;
1893
1894         return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1895                         listing, &bytes, cret);
1896 }
1897
1898
1899 /* SNET */
1900 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1901 {
1902         if (!cret) return -2;
1903         if (!listing) return -2;
1904
1905         return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1906                         NULL, NULL, cret);
1907 }
1908
1909
1910 /* REQT */
1911 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1912 {
1913         char aaa[16];
1914
1915         if (!cret) return -2;
1916         if (session < 0) return -2;
1917
1918         sprintf(aaa, "REQT %d", session);
1919         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1920 }
1921
1922
1923 /* SEEN */
1924 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1925 {
1926         char aaa[27];
1927
1928         if (!cret) return -2;
1929         if (msgnum < 0) return -2;
1930
1931         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1932         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1933 }
1934
1935
1936 /* STLS */
1937 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1938 {
1939         int a;
1940         int r;
1941         char buf[SIZ];
1942
1943 #ifdef HAVE_OPENSSL
1944         SSL *temp_ssl;
1945
1946         /* New SSL object */
1947         temp_ssl = SSL_new(ssl_ctx);
1948         if (!temp_ssl) {
1949                 error_printf("SSL_new failed: %s\n",
1950                                 ERR_reason_error_string(ERR_get_error()));
1951                 return -2;
1952         }
1953         /* Pointless flag waving */
1954 #if SSLEAY_VERSION_NUMBER >= 0x0922
1955         SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1956 #endif
1957
1958         if (!access(EGD_POOL, F_OK))
1959                 RAND_egd(EGD_POOL);
1960
1961         if (!RAND_status()) {
1962                 error_printf("PRNG not properly seeded\n");
1963                 return -2;
1964         }
1965
1966         /* Associate network connection with SSL object */
1967         if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1968                 error_printf("SSL_set_fd failed: %s\n",
1969                                 ERR_reason_error_string(ERR_get_error()));
1970                 return -2;
1971         }
1972
1973         if (status_hook != NULL)
1974                 status_hook("Requesting encryption...\r");
1975
1976         /* Ready to start SSL/TLS */
1977         /* Old code
1978         CtdlIPC_putline(ipc, "STLS");
1979         CtdlIPC_getline(ipc, buf);
1980         if (buf[0] != '2') {
1981                 error_printf("Server can't start TLS: %s\n", buf);
1982                 return 0;
1983         }
1984         */
1985         r = CtdlIPCGenericCommand(ipc,
1986                                   "STLS", NULL, 0, NULL, NULL, cret);
1987         if (r / 100 != 2) {
1988                 error_printf("Server can't start TLS: %s\n", buf);
1989                 endtls(temp_ssl);
1990                 return r;
1991         }
1992
1993         /* Do SSL/TLS handshake */
1994         if ((a = SSL_connect(temp_ssl)) < 1) {
1995                 error_printf("SSL_connect failed: %s\n",
1996                                 ERR_reason_error_string(ERR_get_error()));
1997                 endtls(temp_ssl);
1998                 return -2;
1999         }
2000         ipc->ssl = temp_ssl;
2001
2002         BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2003         {
2004                 int bits, alg_bits;
2005
2006                 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2007                 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2008                                 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2009                                 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2010                                 bits, alg_bits);
2011         }
2012         return r;
2013 #else
2014         return 0;
2015 #endif /* HAVE_OPENSSL */
2016 }
2017
2018
2019 #ifdef HAVE_OPENSSL
2020 static void endtls(SSL *ssl)
2021 {
2022         if (ssl) {
2023                 SSL_shutdown(ssl);
2024                 SSL_free(ssl);
2025         }
2026 }
2027 #endif
2028
2029
2030 /* QDIR */
2031 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2032 {
2033     register int ret;
2034         char *aaa;
2035
2036         if (!address) return -2;
2037         if (!cret) return -2;
2038
2039         aaa = (char *)malloc(strlen(address) + 6);
2040         if (!aaa) return -1;
2041
2042         sprintf(aaa, "QDIR %s", address);
2043         ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2044     free(aaa);
2045     return ret;
2046 }
2047
2048
2049 /* IPGM */
2050 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2051 {
2052         char aaa[30];
2053
2054         if (!cret) return -2;
2055         sprintf(aaa, "IPGM %d", secret);
2056         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2057 }
2058
2059
2060 /* FSCK */
2061 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2062 {
2063         size_t size = 0;
2064
2065         if (!cret) return -2;
2066         if (!mret) return -2;
2067         if (*mret) return -2;
2068
2069         return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2070 }
2071
2072
2073 /*
2074  * Not implemented:
2075  * 
2076  * CHAT
2077  * ETLS
2078  * EXPI
2079  * GTLS
2080  * IGAB
2081  * MSG3
2082  * MSG4
2083  * NDOP
2084  * NETP
2085  * NUOP
2086  * SMTP
2087  */
2088
2089
2090 /* ************************************************************************** */
2091 /*             Stuff below this line is not for public consumption            */
2092 /* ************************************************************************** */
2093
2094
2095 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2096 {
2097         if (ipc->network_status_cb) ipc->network_status_cb(1);
2098 #ifdef THREADED_CLIENT
2099         pthread_mutex_lock(&(ipc->mutex));
2100 #endif
2101 }
2102
2103
2104 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2105 {
2106 #ifdef THREADED_CLIENT
2107         pthread_mutex_unlock(&(ipc->mutex));
2108 #endif
2109         if (ipc->network_status_cb) ipc->network_status_cb(0);
2110 }
2111
2112
2113 /* Read a listing from the server up to 000.  Append to dest if it exists */
2114 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2115 {
2116         size_t length = 0;
2117         size_t linelength;
2118         char *ret = NULL;
2119         char aaa[SIZ];
2120
2121         ret = dest;
2122         if (ret != NULL) {
2123                 length = strlen(ret);
2124         } else {
2125                 length = 0;
2126         }
2127
2128         while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2129                 linelength = strlen(aaa);
2130                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2131                 if (ret) {
2132                         strcpy(&ret[length], aaa);
2133                         length += linelength;
2134                         strcpy(&ret[length++], "\n");
2135                 }
2136         }
2137
2138         return(ret);
2139 }
2140
2141
2142 /* Send a listing to the server; generate the ending 000. */
2143 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2144 {
2145         char *text;
2146
2147         text = (char *)malloc(strlen(listing) + 6);
2148         if (text) {
2149                 strcpy(text, listing);
2150                 while (text[strlen(text) - 1] == '\n')
2151                         text[strlen(text) - 1] = '\0';
2152                 strcat(text, "\n000");
2153                 CtdlIPC_putline(ipc, text);
2154                 free(text);
2155                 text = NULL;
2156         } else {
2157                 /* Malloc failed but we are committed to send */
2158                 /* This may result in extra blanks at the bottom */
2159                 CtdlIPC_putline(ipc, text);
2160                 CtdlIPC_putline(ipc, "000");
2161         }
2162         return 0;
2163 }
2164
2165
2166 /* Partial read of file from server */
2167 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2168 {
2169         register size_t len = 0;
2170         char aaa[SIZ];
2171
2172         if (!buf) return 0;
2173         if (!cret) return 0;
2174         if (bytes < 1) return 0;
2175
2176         CtdlIPC_lock(ipc);
2177         sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2178         CtdlIPC_putline(ipc, aaa);
2179         CtdlIPC_getline(ipc, aaa);
2180         if (aaa[0] != '6')
2181                 strcpy(cret, &aaa[4]);
2182         else {
2183                 len = extract_long(&aaa[4], 0);
2184                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2185                 if (*buf) {
2186                         /* I know what I'm doing */
2187                         serv_read(ipc, ((char *)(*buf) + offset), len);
2188                 } else {
2189                         /* We have to read regardless */
2190                         serv_read(ipc, aaa, len);
2191                         len = 0;
2192                 }
2193         }
2194         CtdlIPC_unlock(ipc);
2195         return len;
2196 }
2197
2198
2199 /* CLOS */
2200 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2201 {
2202         register int ret;
2203
2204         if (!cret) return -2;
2205         if (!ipc->downloading) return -2;
2206
2207         ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2208         if (ret / 100 == 2)
2209                 ipc->downloading = 0;
2210         return ret;
2211 }
2212
2213
2214 /* MSGP */
2215 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2216         register int ret;
2217         char cmd[SIZ];
2218         
2219         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2220         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2221         return ret;
2222 }
2223
2224
2225
2226 /* READ */
2227 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2228                 void (*progress_gauge_callback)
2229                         (CtdlIPC*, unsigned long, unsigned long),
2230                char *cret)
2231 {
2232         register size_t len;
2233
2234         if (!cret) return -1;
2235         if (!buf) return -1;
2236         if (*buf) return -1;
2237         if (!ipc->downloading) return -1;
2238
2239         len = resume;
2240         if (progress_gauge_callback)
2241                 progress_gauge_callback(ipc, len, bytes);
2242         while (len < bytes) {
2243                 register size_t block;
2244
2245                 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2246                 if (block == 0) {
2247                         free(*buf);
2248                         return 0;
2249                 }
2250                 len += block;
2251                 if (progress_gauge_callback)
2252                         progress_gauge_callback(ipc, len, bytes);
2253         }
2254         return len;
2255 }
2256
2257 /* READ - pipelined */
2258 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2259                size_t resume,
2260                 void (*progress_gauge_callback)
2261                         (CtdlIPC*, unsigned long, unsigned long),
2262                char *cret)
2263 {
2264         register size_t len;
2265         register int calls;     /* How many calls in the pipeline */
2266         register int i;         /* iterator */
2267         char aaa[4096];
2268
2269         if (!cret) return -1;
2270         if (!buf) return -1;
2271         if (*buf) return -1;
2272         if (!ipc->downloading) return -1;
2273
2274         *buf = (void *)realloc(*buf, bytes - resume);
2275         if (!*buf) return -1;
2276
2277         len = 0;
2278         CtdlIPC_lock(ipc);
2279         if (progress_gauge_callback)
2280                 progress_gauge_callback(ipc, len, bytes);
2281
2282         /* How many calls will be in the pipeline? */
2283         calls = (bytes - resume) / 4096;
2284         if ((bytes - resume) % 4096) calls++;
2285
2286         /* Send all requests at once */
2287         for (i = 0; i < calls; i++) {
2288                 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2289                 CtdlIPC_putline(ipc, aaa);
2290         }
2291
2292         /* Receive all responses at once */
2293         for (i = 0; i < calls; i++) {
2294                 CtdlIPC_getline(ipc, aaa);
2295                 if (aaa[0] != '6')
2296                         strcpy(cret, &aaa[4]);
2297                 else {
2298                         len = extract_long(&aaa[4], 0);
2299                         /* I know what I'm doing */
2300                         serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2301                 }
2302                 if (progress_gauge_callback)
2303                         progress_gauge_callback(ipc, i * 4096 + len, bytes);
2304         }
2305         CtdlIPC_unlock(ipc);
2306         return len;
2307 }
2308
2309
2310 /* UCLS */
2311 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2312 {
2313         register int ret;
2314         char cmd[8];
2315
2316         if (!cret) return -1;
2317         if (!ipc->uploading) return -1;
2318
2319         sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2320         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2321         ipc->uploading = 0;
2322         return ret;
2323 }
2324
2325
2326 /* WRIT */
2327 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2328                 void (*progress_gauge_callback)
2329                         (CtdlIPC*, unsigned long, unsigned long),
2330                 char *cret)
2331 {
2332         register int ret = -1;
2333         register size_t offset = 0;
2334         size_t bytes;
2335         char aaa[SIZ];
2336         char buf[4096];
2337         FILE *fd;
2338
2339         if (!cret) return -1;
2340         if (!path) return -1;
2341         if (!*path) return -1;
2342
2343         fd = fopen(path, "r");
2344         if (!fd) return -2;
2345
2346         fseek(fd, 0L, SEEK_END);
2347         bytes = ftell(fd);
2348         rewind(fd);
2349
2350         if (progress_gauge_callback)
2351                 progress_gauge_callback(ipc, 0, bytes);
2352
2353         while (offset < bytes) {
2354                 register size_t to_write;
2355
2356                 /* Read some data in */
2357                 to_write = fread(buf, 1, 4096, fd);
2358                 if (!to_write) {
2359                         if (feof(fd) || ferror(fd)) break;
2360                 }
2361                 sprintf(aaa, "WRIT %d", (int)to_write);
2362                 CtdlIPC_putline(ipc, aaa);
2363                 CtdlIPC_getline(ipc, aaa);
2364                 strcpy(cret, &aaa[4]);
2365                 ret = atoi(aaa);
2366                 if (aaa[0] == '7') {
2367                         to_write = extract_long(&aaa[4], 0);
2368                         
2369                         serv_write(ipc, buf, to_write);
2370                         offset += to_write;
2371                         if (progress_gauge_callback)
2372                                 progress_gauge_callback(ipc, offset, bytes);
2373                         /* Detect short reads and back up if needed */
2374                         /* offset will never be negative anyway */
2375                         fseek(fd, (signed)offset, SEEK_SET);
2376                 } else {
2377                         break;
2378                 }
2379         }
2380         if (progress_gauge_callback)
2381                 progress_gauge_callback(ipc, 1, 1);
2382         return (!ferror(fd) ? ret : -2);
2383 }
2384
2385
2386 /*
2387  * Generic command method.  This method should handle any server command
2388  * except for CHAT.  It takes the following arguments:
2389  *
2390  * ipc                  The server to speak with
2391  * command              Preformatted command to send to server
2392  * to_send              A text or binary file to send to server
2393  *                      (only sent if server requests it)
2394  * bytes_to_send        The number of bytes in to_send (required if
2395  *                      sending binary, optional if sending listing)
2396  * to_receive           Pointer to a NULL pointer, if the server
2397  *                      sends text or binary we will allocate memory
2398  *                      for the file and stuff it here
2399  * bytes_to_receive     If a file is received, we will store its
2400  *                      byte count here
2401  * proto_response       The protocol response.  Caller must provide
2402  *                      this buffer and ensure that it is at least
2403  *                      128 bytes in length.
2404  *
2405  * This function returns a number equal to the protocol response number,
2406  * -1 if an internal error occurred, -2 if caller provided bad values,
2407  * or 0 - the protocol response number if bad values were found during
2408  * the protocol exchange.
2409  * It stores the protocol response string (minus the number) in 
2410  * protocol_response as described above.  Some commands send additional
2411  * data in this string.
2412  */
2413 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2414                 const char *command, const char *to_send,
2415                 size_t bytes_to_send, char **to_receive, 
2416                 size_t *bytes_to_receive, char *proto_response)
2417 {
2418         char buf[SIZ];
2419         register int ret;
2420         int watch_ssl = 0;
2421
2422         if (!command) return -2;
2423         if (!proto_response) return -2;
2424
2425 #ifdef HAVE_OPENSSL
2426         if (ipc->ssl) watch_ssl = 1;
2427 #endif
2428
2429         CtdlIPC_lock(ipc);
2430         CtdlIPC_putline(ipc, command);
2431         while (1) {
2432                 CtdlIPC_getline(ipc, proto_response);
2433                 if (proto_response[3] == '*')
2434                         instant_msgs = 1;
2435                 ret = atoi(proto_response);
2436                 strcpy(proto_response, &proto_response[4]);
2437                 switch (ret / 100) {
2438                 default:                        /* Unknown, punt */
2439                 case 2:                         /* OK */
2440                 case 3:                         /* MORE_DATA */
2441                 case 5:                         /* ERROR */
2442                         /* Don't need to do anything */
2443                         break;
2444                 case 1:                         /* LISTING_FOLLOWS */
2445                         if (to_receive && !*to_receive && bytes_to_receive) {
2446                                 *to_receive = CtdlIPCReadListing(ipc, NULL);
2447                         } else { /* Drain */
2448                                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2449                                 ret = -ret;
2450                         }
2451                         break;
2452                 case 4:                         /* SEND_LISTING */
2453                         if (to_send) {
2454                                 CtdlIPCSendListing(ipc, to_send);
2455                         } else {
2456                                 /* No listing given, fake it */
2457                                 CtdlIPC_putline(ipc, "000");
2458                                 ret = -ret;
2459                         }
2460                         break;
2461                 case 6:                         /* BINARY_FOLLOWS */
2462                         if (to_receive && !*to_receive && bytes_to_receive) {
2463                                 *bytes_to_receive =
2464                                         extract_long(proto_response, 0);
2465                                 *to_receive = (char *)
2466                                         malloc((size_t)*bytes_to_receive);
2467                                 if (!*to_receive) {
2468                                         ret = -1;
2469                                 } else {
2470                                         serv_read(ipc, *to_receive,
2471                                                         *bytes_to_receive);
2472                                 }
2473                         } else {
2474                                 /* Drain */
2475                                 size_t drain;
2476
2477                                 drain = extract_long(proto_response, 0);
2478                                 while (drain > SIZ) {
2479                                         serv_read(ipc, buf, SIZ);
2480                                         drain -= SIZ;
2481                                 }
2482                                 serv_read(ipc, buf, drain);
2483                                 ret = -ret;
2484                         }
2485                         break;
2486                 case 7:                         /* SEND_BINARY */
2487                         if (to_send && bytes_to_send) {
2488                                 serv_write(ipc, to_send, bytes_to_send);
2489                         } else if (bytes_to_send) {
2490                                 /* Fake it, send nulls */
2491                                 size_t fake;
2492
2493                                 fake = bytes_to_send;
2494                                 memset(buf, '\0', SIZ);
2495                                 while (fake > SIZ) {
2496                                         serv_write(ipc, buf, SIZ);
2497                                         fake -= SIZ;
2498                                 }
2499                                 serv_write(ipc, buf, fake);
2500                                 ret = -ret;
2501                         } /* else who knows?  DANGER WILL ROBINSON */
2502                         break;
2503                 case 8:                         /* START_CHAT_MODE */
2504                         if (!strncasecmp(command, "CHAT", 4)) {
2505                                 /* Don't call chatmode with generic! */
2506                                 CtdlIPC_putline(ipc, "/quit");
2507                                 ret = -ret;
2508                         } else {
2509                                 /* In this mode we send then receive listing */
2510                                 if (to_send) {
2511                                         CtdlIPCSendListing(ipc, to_send);
2512                                 } else {
2513                                         /* No listing given, fake it */
2514                                         CtdlIPC_putline(ipc, "000");
2515                                         ret = -ret;
2516                                 }
2517                                 if (to_receive && !*to_receive
2518                                                 && bytes_to_receive) {
2519                                         *to_receive = CtdlIPCReadListing(ipc, NULL);
2520                                 } else { /* Drain */
2521                                         while (CtdlIPC_getline(ipc, buf),
2522                                                         strcmp(buf, "000")) ;
2523                                         ret = -ret;
2524                                 }
2525                         }
2526                         break;
2527                 case 9:                         /* ASYNC_MSG */
2528                         /* CtdlIPCDoAsync(ret, proto_response); */
2529                         free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
2530                         break;
2531                 }
2532                 if (ret / 100 != 9)
2533                         break;
2534         }
2535         CtdlIPC_unlock(ipc);
2536         return ret;
2537 }
2538
2539
2540 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2541 {
2542         struct hostent *phe;
2543         struct servent *pse;
2544         struct protoent *ppe;
2545         struct sockaddr_in sin;
2546         int s, type;
2547
2548         memset(&sin, 0, sizeof(sin));
2549         sin.sin_family = AF_INET;
2550
2551         pse = getservbyname(service, protocol);
2552         if (pse != NULL) {
2553                 sin.sin_port = pse->s_port;
2554         }
2555         else if (atoi(service) > 0) {
2556                 sin.sin_port = htons(atoi(service));
2557         }
2558         else {
2559                 sin.sin_port = htons(defaultPort);
2560         }
2561         phe = gethostbyname(host);
2562         if (phe) {
2563                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2564         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2565                 return -1;
2566         }
2567         if ((ppe = getprotobyname(protocol)) == 0) {
2568                 return -1;
2569         }
2570         if (!strcmp(protocol, "udp")) {
2571                 type = SOCK_DGRAM;
2572         } else {
2573                 type = SOCK_STREAM;
2574         }
2575
2576         s = socket(PF_INET, type, ppe->p_proto);
2577         if (s < 0) {
2578                 return -1;
2579         }
2580
2581         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2582                 close(s);
2583                 return -1;
2584         }
2585
2586         return (s);
2587 }
2588
2589 static int uds_connectsock(int *isLocal, char *sockpath)
2590 {
2591         struct sockaddr_un addr;
2592         int s;
2593
2594         memset(&addr, 0, sizeof(addr));
2595         addr.sun_family = AF_UNIX;
2596         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2597
2598         s = socket(AF_UNIX, SOCK_STREAM, 0);
2599         if (s < 0) {
2600                 return -1;
2601         }
2602
2603         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2604                 close(s);
2605                 return -1;
2606         }
2607
2608         *isLocal = 1;
2609         return s;
2610 }
2611
2612
2613 /*
2614  * input binary data from socket
2615  */
2616 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2617 {
2618         unsigned int len, rlen;
2619
2620 #if defined(HAVE_OPENSSL)
2621         if (ipc->ssl) {
2622                 serv_read_ssl(ipc, buf, bytes);
2623                 return;
2624         }
2625 #endif
2626         len = 0;
2627         while (len < bytes) {
2628                 rlen = read(ipc->sock, &buf[len], bytes - len);
2629                 if (rlen < 1) {
2630                         connection_died(ipc, 0);
2631                         return;
2632                 }
2633                 len += rlen;
2634         }
2635 }
2636
2637
2638 /*
2639  * send binary to server
2640  */
2641 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2642 {
2643         unsigned int bytes_written = 0;
2644         int retval;
2645
2646 #if defined(HAVE_OPENSSL)
2647         if (ipc->ssl) {
2648                 serv_write_ssl(ipc, buf, nbytes);
2649                 return;
2650         }
2651 #endif
2652         while (bytes_written < nbytes) {
2653                 retval = write(ipc->sock, &buf[bytes_written],
2654                                nbytes - bytes_written);
2655                 if (retval < 1) {
2656                         connection_died(ipc, 0);
2657                         return;
2658                 }
2659                 bytes_written += retval;
2660         }
2661 }
2662
2663
2664 #ifdef HAVE_OPENSSL
2665 /*
2666  * input binary data from encrypted connection
2667  */
2668 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2669 {
2670         int len, rlen;
2671         char junk[1];
2672
2673         len = 0;
2674         while (len < bytes) {
2675                 if (SSL_want_read(ipc->ssl)) {
2676                         if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2677                                 error_printf("SSL_write in serv_read:\n");
2678                                 ERR_print_errors_fp(stderr);
2679                         }
2680                 }
2681                 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2682                 if (rlen < 1) {
2683                         long errval;
2684
2685                         errval = SSL_get_error(ipc->ssl, rlen);
2686                         if (errval == SSL_ERROR_WANT_READ ||
2687                                         errval == SSL_ERROR_WANT_WRITE) {
2688                                 sleep(1);
2689                                 continue;
2690                         }
2691 /***
2692  Not sure why we'd want to handle these error codes any differently,
2693  but this definitely isn't the way to handle them.  Someone must have
2694  naively assumed that we could fall back to unencrypted communications,
2695  but all it does is just recursively blow the stack.
2696                         if (errval == SSL_ERROR_ZERO_RETURN ||
2697                                         errval == SSL_ERROR_SSL) {
2698                                 serv_read(ipc, &buf[len], bytes - len);
2699                                 return;
2700                         }
2701  ***/
2702                         error_printf("SSL_read in serv_read: %s\n",
2703                                         ERR_reason_error_string(ERR_peek_error()));
2704                         connection_died(ipc, 1);
2705                         return;
2706                 }
2707                 len += rlen;
2708         }
2709 }
2710
2711
2712 /*
2713  * send binary to server encrypted
2714  */
2715 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2716 {
2717         unsigned int bytes_written = 0;
2718         int retval;
2719         char junk[1];
2720
2721         while (bytes_written < nbytes) {
2722                 if (SSL_want_write(ipc->ssl)) {
2723                         if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2724                                 error_printf("SSL_read in serv_write:\n");
2725                                 ERR_print_errors_fp(stderr);
2726                         }
2727                 }
2728                 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2729                                 nbytes - bytes_written);
2730                 if (retval < 1) {
2731                         long errval;
2732
2733                         errval = SSL_get_error(ipc->ssl, retval);
2734                         if (errval == SSL_ERROR_WANT_READ ||
2735                                         errval == SSL_ERROR_WANT_WRITE) {
2736                                 sleep(1);
2737                                 continue;
2738                         }
2739                         if (errval == SSL_ERROR_ZERO_RETURN ||
2740                                         errval == SSL_ERROR_SSL) {
2741                                 serv_write(ipc, &buf[bytes_written],
2742                                                 nbytes - bytes_written);
2743                                 return;
2744                         }
2745                         error_printf("SSL_write in serv_write: %s\n",
2746                                         ERR_reason_error_string(ERR_peek_error()));
2747                         connection_died(ipc, 1);
2748                         return;
2749                 }
2750                 bytes_written += retval;
2751         }
2752 }
2753
2754
2755 static void CtdlIPC_init_OpenSSL(void)
2756 {
2757         int a;
2758         SSL_METHOD *ssl_method;
2759         DH *dh;
2760         
2761         /* already done init */
2762         if (ssl_ctx) {
2763                 return;
2764         }
2765
2766         /* Get started */
2767         a = 0;
2768         ssl_ctx = NULL;
2769         dh = NULL;
2770         SSL_load_error_strings();
2771         SSLeay_add_ssl_algorithms();
2772
2773         /* Set up the SSL context in which we will oeprate */
2774         ssl_method = SSLv23_client_method();
2775         ssl_ctx = SSL_CTX_new(ssl_method);
2776         if (!ssl_ctx) {
2777                 error_printf("SSL_CTX_new failed: %s\n",
2778                                 ERR_reason_error_string(ERR_get_error()));
2779                 return;
2780         }
2781         /* Any reasonable cipher we can get */
2782         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2783                 error_printf("No ciphers available for encryption\n");
2784                 return;
2785         }
2786         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2787         
2788         /* Load DH parameters into the context */
2789         dh = DH_new();
2790         if (!dh) {
2791                 error_printf("Can't allocate a DH object: %s\n",
2792                                 ERR_reason_error_string(ERR_get_error()));
2793                 return;
2794         }
2795         if (!(BN_hex2bn(&(dh->p), DH_P))) {
2796                 error_printf("Can't assign DH_P: %s\n",
2797                                 ERR_reason_error_string(ERR_get_error()));
2798                 DH_free(dh);
2799                 return;
2800         }
2801         if (!(BN_hex2bn(&(dh->g), DH_G))) {
2802                 error_printf("Can't assign DH_G: %s\n",
2803                                 ERR_reason_error_string(ERR_get_error()));
2804                 DH_free(dh);
2805                 return;
2806         }
2807         dh->length = DH_L;
2808         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2809         DH_free(dh);
2810
2811 #ifdef THREADED_CLIENT
2812         /* OpenSSL requires callbacks for threaded clients */
2813         CRYPTO_set_locking_callback(ssl_lock);
2814         CRYPTO_set_id_callback(id_callback);
2815
2816         /* OpenSSL requires us to do semaphores for threaded clients */
2817         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2818         if (!Critters) {
2819                 perror("malloc failed");
2820                 exit(1);
2821         } else {
2822                 for (a = 0; a < CRYPTO_num_locks(); a++) {
2823                         Critters[a] = malloc(sizeof (pthread_mutex_t));
2824                         if (!Critters[a]) {
2825                                 perror("malloc failed");
2826                                 exit(1);
2827                         }
2828                         pthread_mutex_init(Critters[a], NULL);
2829                 }
2830         }
2831 #endif /* THREADED_CLIENT */       
2832 }
2833
2834
2835 static void ssl_lock(int mode, int n, const char *file, int line)
2836 {
2837 #ifdef THREADED_CLIENT
2838         if (mode & CRYPTO_LOCK)
2839                 pthread_mutex_lock(Critters[n]);
2840         else
2841                 pthread_mutex_unlock(Critters[n]);
2842 #endif /* THREADED_CLIENT */
2843 }
2844
2845 #ifdef THREADED_CLIENT
2846 static unsigned long id_callback(void) {
2847         return (unsigned long)pthread_self();
2848 }
2849 #endif /* THREADED_CLIENT */
2850 #endif /* HAVE_OPENSSL */
2851
2852
2853 /*
2854  * input string from socket - implemented in terms of serv_read()
2855  */
2856 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2857 {
2858         int i;
2859
2860         /* Read one character at a time. */
2861         for (i = 0;; i++) {
2862                 serv_read(ipc, &buf[i], 1);
2863                 if (buf[i] == '\n' || i == (SIZ-1))
2864                         break;
2865         }
2866
2867         /* If we got a long line, discard characters until the newline. */
2868         if (i == (SIZ-1))
2869                 while (buf[i] != '\n')
2870                         serv_read(ipc, &buf[i], 1);
2871
2872         /* Strip the trailing newline (and carriage return, if present) */
2873         if (buf[i] == 10) buf[i--] = 0;
2874         if (buf[i] == 13) buf[i--] = 0;
2875 }
2876
2877 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2878 {
2879         CtdlIPC_getline(ipc, buf);
2880 }
2881
2882 /*
2883  * send line to server - implemented in terms of serv_write()
2884  */
2885 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2886 {
2887         char *cmd = NULL;
2888         int len;
2889
2890         len = strlen(buf);
2891         cmd = malloc(len + 2);
2892         if (!cmd) {
2893                 /* This requires no extra memory */
2894                 serv_write(ipc, buf, len);
2895                 serv_write(ipc, "\n", 1);
2896         } else {
2897                 /* This is network-optimized */
2898                 strncpy(cmd, buf, len);
2899                 strcpy(cmd + len, "\n");
2900                 serv_write(ipc, cmd, len + 1);
2901                 free(cmd);
2902         }
2903
2904         ipc->last_command_sent = time(NULL);
2905 }
2906
2907 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
2908 {
2909         CtdlIPC_putline(ipc, buf);
2910 }
2911
2912
2913 /*
2914  * attach to server
2915  */
2916 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2917 {
2918         int a;
2919         char cithost[SIZ];
2920         char citport[SIZ];
2921         char sockpath[SIZ];
2922         CtdlIPC* ipc;
2923
2924         ipc = ialloc(CtdlIPC);
2925         if (!ipc) {
2926                 return 0;
2927         }
2928 #if defined(HAVE_OPENSSL)
2929         ipc->ssl = NULL;
2930         CtdlIPC_init_OpenSSL();
2931 #endif
2932 #if defined(HAVE_PTHREAD_H)
2933         pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2934 #endif
2935         ipc->sock = -1;                 /* Not connected */
2936         ipc->isLocal = 0;               /* Not local, of course! */
2937         ipc->downloading = 0;
2938         ipc->uploading = 0;
2939         ipc->last_command_sent = 0L;
2940         ipc->network_status_cb = NULL;
2941
2942         strcpy(cithost, DEFAULT_HOST);  /* default host */
2943         strcpy(citport, DEFAULT_PORT);  /* default port */
2944
2945         /* Allow caller to supply our values (Windows) */
2946         if (hostbuf && strlen(hostbuf) > 0)
2947                 strcpy(cithost, hostbuf);
2948         if (portbuf && strlen(portbuf) > 0)
2949                 strcpy(citport, portbuf);
2950
2951         /* Read host/port from command line if present */
2952         for (a = 0; a < argc; ++a) {
2953                 if (a == 0) {
2954                         /* do nothing */
2955                 } else if (a == 1) {
2956                         strcpy(cithost, argv[a]);
2957                 } else if (a == 2) {
2958                         strcpy(citport, argv[a]);
2959                 } else {
2960                         error_printf("%s: usage: ",argv[0]);
2961                         error_printf("%s [host] [port] ",argv[0]);
2962                         ifree(ipc);
2963                         errno = EINVAL;
2964                         return 0;
2965                 }
2966         }
2967
2968         if ((!strcmp(cithost, "localhost"))
2969            || (!strcmp(cithost, "127.0.0.1"))) {
2970                 ipc->isLocal = 1;
2971         }
2972
2973         /* If we're using a unix domain socket we can do a bunch of stuff */
2974         if (!strcmp(cithost, UDS)) {
2975                 if (!strcasecmp(citport, DEFAULT_PORT)) {
2976                         snprintf(sockpath, sizeof sockpath, file_citadel_socket);
2977                 }
2978                 else {
2979                         snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
2980                 }
2981                 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2982                 if (ipc->sock == -1) {
2983                         ifree(ipc);
2984                         return 0;
2985                 }
2986                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2987                 if (portbuf != NULL) strcpy(portbuf, sockpath);
2988                 return ipc;
2989         }
2990
2991         ipc->sock = connectsock(cithost, citport, "tcp", 504);
2992         if (ipc->sock == -1) {
2993                 ifree(ipc);
2994                 return 0;
2995         }
2996         if (hostbuf != NULL) strcpy(hostbuf, cithost);
2997         if (portbuf != NULL) strcpy(portbuf, citport);
2998         return ipc;
2999 }
3000
3001
3002 /*
3003  * Disconnect and delete the IPC class (destructor)
3004  */
3005 void CtdlIPC_delete(CtdlIPC* ipc)
3006 {
3007 #ifdef HAVE_OPENSSL
3008         if (ipc->ssl) {
3009                 SSL_shutdown(ipc->ssl);
3010                 SSL_free(ipc->ssl);
3011                 ipc->ssl = NULL;
3012         }
3013 #endif
3014         if (ipc->sock > -1) {
3015                 shutdown(ipc->sock, 2); /* Close it up */
3016                 ipc->sock = -1;
3017         }
3018         ifree(ipc);
3019 }
3020
3021
3022 /*
3023  * Disconnect and delete the IPC class (destructor)
3024  * Also NULLs out the pointer
3025  */
3026 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3027 {
3028         CtdlIPC_delete(*pipc);
3029         *pipc = NULL;
3030 }
3031
3032
3033 /*
3034  * return the file descriptor of the server socket so we can select() on it.
3035  *
3036  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3037  * rewritten...
3038  */
3039 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3040 {
3041         return ipc->sock;
3042 }
3043
3044
3045 /*
3046  * return one character
3047  *
3048  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3049  * rewritten...
3050  */
3051 char CtdlIPC_get(CtdlIPC* ipc)
3052 {
3053         char buf[2];
3054         char ch;
3055
3056         serv_read(ipc, buf, 1);
3057         ch = (int) buf[0];
3058
3059         return (ch);
3060 }