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