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