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