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