]> code.citadel.org Git - citadel.git/blob - citadel/citadel_ipc.c
citadel_ipc.c: when fetching an expire policy, the command buffer needs to be
[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 cmd[256];
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(cmd, "GPEX %s", proto[which]);
1773         ret = CtdlIPCGenericCommand(ipc, cmd, 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 /* SPEX */
1783 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1784 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1785 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1786                 struct ExpirePolicy *policy, char *cret)
1787 {
1788         char aaa[38];
1789         char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1790
1791         if (!cret) return -2;
1792         if (which < 0 || which > 3) return -2;
1793         if (!policy) return -2;
1794         if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1795         if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1796
1797         sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1798                         policy->expire_mode, policy->expire_value);
1799         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1800 }
1801
1802
1803 /* CONF GET */
1804 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1805 {
1806         size_t bytes;
1807
1808         if (!cret) return -2;
1809         if (!listing) return -2;
1810         if (*listing) return -2;
1811
1812         return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1813                         listing, &bytes, cret);
1814 }
1815
1816
1817 /* CONF SET */
1818 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1819 {
1820         if (!cret) return -2;
1821         if (!listing) return -2;
1822
1823         return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1824                         NULL, NULL, cret);
1825 }
1826
1827
1828 /* CONF GETSYS */
1829 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1830                 char **listing, char *cret)
1831 {
1832         char *aaa;
1833         size_t bytes;
1834
1835         if (!cret) return -2;
1836         if (!mimetype) return -2;
1837         if (!listing) return -2;
1838         if (*listing) return -2;
1839
1840         aaa = malloc(strlen(mimetype) + 13);
1841         if (!aaa) return -1;
1842         sprintf(aaa, "CONF GETSYS|%s", mimetype);
1843         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1844                         listing, &bytes, cret);
1845 }
1846
1847
1848 /* CONF PUTSYS */
1849 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1850                const char *listing, char *cret)
1851 {
1852         char *aaa;
1853
1854         if (!cret) return -2;
1855         if (!mimetype) return -2;
1856         if (!listing) return -2;
1857
1858         aaa = malloc(strlen(mimetype) + 13);
1859         if (!aaa) return -1;
1860         sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1861         return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1862                         NULL, NULL, cret);
1863 }
1864
1865
1866 /* GNET */
1867 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1868 {
1869         size_t bytes;
1870
1871         if (!cret) return -2;
1872         if (!listing) return -2;
1873         if (*listing) return -2;
1874
1875         return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1876                         listing, &bytes, cret);
1877 }
1878
1879
1880 /* SNET */
1881 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1882 {
1883         if (!cret) return -2;
1884         if (!listing) return -2;
1885
1886         return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1887                         NULL, NULL, cret);
1888 }
1889
1890
1891 /* REQT */
1892 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1893 {
1894         char aaa[16];
1895
1896         if (!cret) return -2;
1897         if (session < 0) return -2;
1898
1899         sprintf(aaa, "REQT %d", session);
1900         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1901 }
1902
1903
1904 /* SEEN */
1905 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1906 {
1907         char aaa[27];
1908
1909         if (!cret) return -2;
1910         if (msgnum < 0) return -2;
1911
1912         sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1913         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1914 }
1915
1916
1917 /* STLS */
1918 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1919 {
1920         int a;
1921         int r;
1922         char buf[SIZ];
1923
1924 #ifdef HAVE_OPENSSL
1925         SSL *temp_ssl;
1926
1927         /* New SSL object */
1928         temp_ssl = SSL_new(ssl_ctx);
1929         if (!temp_ssl) {
1930                 error_printf("SSL_new failed: %s\n",
1931                                 ERR_reason_error_string(ERR_get_error()));
1932                 return -2;
1933         }
1934         /* Pointless flag waving */
1935 #if SSLEAY_VERSION_NUMBER >= 0x0922
1936         SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1937 #endif
1938
1939         if (!access(EGD_POOL, F_OK))
1940                 RAND_egd(EGD_POOL);
1941
1942         if (!RAND_status()) {
1943                 error_printf("PRNG not properly seeded\n");
1944                 return -2;
1945         }
1946
1947         /* Associate network connection with SSL object */
1948         if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1949                 error_printf("SSL_set_fd failed: %s\n",
1950                                 ERR_reason_error_string(ERR_get_error()));
1951                 return -2;
1952         }
1953
1954         if (status_hook != NULL)
1955                 status_hook("Requesting encryption...\r");
1956
1957         /* Ready to start SSL/TLS */
1958         /* Old code
1959         CtdlIPC_putline(ipc, "STLS");
1960         CtdlIPC_getline(ipc, buf);
1961         if (buf[0] != '2') {
1962                 error_printf("Server can't start TLS: %s\n", buf);
1963                 return 0;
1964         }
1965         */
1966         r = CtdlIPCGenericCommand(ipc,
1967                                   "STLS", NULL, 0, NULL, NULL, cret);
1968         if (r / 100 != 2) {
1969                 error_printf("Server can't start TLS: %s\n", buf);
1970                 endtls(temp_ssl);
1971                 return r;
1972         }
1973
1974         /* Do SSL/TLS handshake */
1975         if ((a = SSL_connect(temp_ssl)) < 1) {
1976                 error_printf("SSL_connect failed: %s\n",
1977                                 ERR_reason_error_string(ERR_get_error()));
1978                 endtls(temp_ssl);
1979                 return -2;
1980         }
1981         ipc->ssl = temp_ssl;
1982
1983         BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1984         {
1985                 int bits, alg_bits;
1986
1987                 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1988                 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1989                                 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1990                                 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
1991                                 bits, alg_bits);
1992         }
1993         return r;
1994 #else
1995         return 0;
1996 #endif /* HAVE_OPENSSL */
1997 }
1998
1999
2000 #ifdef HAVE_OPENSSL
2001 static void endtls(SSL *ssl)
2002 {
2003         if (ssl) {
2004                 SSL_shutdown(ssl);
2005                 SSL_free(ssl);
2006         }
2007 }
2008 #endif
2009
2010
2011 /* QDIR */
2012 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2013 {
2014         char *aaa;
2015
2016         if (!address) return -2;
2017         if (!cret) return -2;
2018
2019         aaa = (char *)malloc(strlen(address) + 6);
2020         if (!aaa) return -1;
2021
2022         sprintf(aaa, "QDIR %s", address);
2023         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2024 }
2025
2026
2027 /* IPGM */
2028 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2029 {
2030         char aaa[30];
2031
2032         if (!cret) return -2;
2033         sprintf(aaa, "IPGM %d", secret);
2034         return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2035 }
2036
2037
2038 /* FSCK */
2039 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2040 {
2041         size_t size = 0;
2042
2043         if (!cret) return -2;
2044         if (!mret) return -2;
2045         if (*mret) return -2;
2046
2047         return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2048 }
2049
2050
2051 /*
2052  * Not implemented:
2053  * 
2054  * CHAT
2055  * ETLS
2056  * EXPI
2057  * GTLS
2058  * IGAB
2059  * MSG3
2060  * MSG4
2061  * NDOP
2062  * NETP
2063  * NUOP
2064  * SMTP
2065  */
2066
2067
2068 /* ************************************************************************** */
2069 /*             Stuff below this line is not for public consumption            */
2070 /* ************************************************************************** */
2071
2072
2073 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2074 {
2075         if (ipc->network_status_cb) ipc->network_status_cb(1);
2076 #ifdef THREADED_CLIENT
2077         pthread_mutex_lock(&(ipc->mutex));
2078 #endif
2079 }
2080
2081
2082 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2083 {
2084 #ifdef THREADED_CLIENT
2085         pthread_mutex_unlock(&(ipc->mutex));
2086 #endif
2087         if (ipc->network_status_cb) ipc->network_status_cb(0);
2088 }
2089
2090
2091 /* Read a listing from the server up to 000.  Append to dest if it exists */
2092 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2093 {
2094         size_t length = 0;
2095         size_t linelength;
2096         char *ret = NULL;
2097         char aaa[SIZ];
2098
2099         ret = dest;
2100         if (ret != NULL) {
2101                 length = strlen(ret);
2102         } else {
2103                 length = 0;
2104         }
2105
2106         while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2107                 linelength = strlen(aaa);
2108                 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2109                 if (ret) {
2110                         strcpy(&ret[length], aaa);
2111                         length += linelength;
2112                         strcpy(&ret[length++], "\n");
2113                 }
2114         }
2115
2116         return(ret);
2117 }
2118
2119
2120 /* Send a listing to the server; generate the ending 000. */
2121 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2122 {
2123         char *text;
2124
2125         text = (char *)malloc(strlen(listing) + 6);
2126         if (text) {
2127                 strcpy(text, listing);
2128                 while (text[strlen(text) - 1] == '\n')
2129                         text[strlen(text) - 1] = '\0';
2130                 strcat(text, "\n000");
2131                 CtdlIPC_putline(ipc, text);
2132                 free(text);
2133                 text = NULL;
2134         } else {
2135                 /* Malloc failed but we are committed to send */
2136                 /* This may result in extra blanks at the bottom */
2137                 CtdlIPC_putline(ipc, text);
2138                 CtdlIPC_putline(ipc, "000");
2139         }
2140         return 0;
2141 }
2142
2143
2144 /* Partial read of file from server */
2145 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2146 {
2147         register size_t len = 0;
2148         char aaa[SIZ];
2149
2150         if (!buf) return 0;
2151         if (!cret) return 0;
2152         if (bytes < 1) return 0;
2153
2154         CtdlIPC_lock(ipc);
2155         sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2156         CtdlIPC_putline(ipc, aaa);
2157         CtdlIPC_getline(ipc, aaa);
2158         if (aaa[0] != '6')
2159                 strcpy(cret, &aaa[4]);
2160         else {
2161                 len = extract_long(&aaa[4], 0);
2162                 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2163                 if (*buf) {
2164                         /* I know what I'm doing */
2165                         serv_read(ipc, ((char *)(*buf) + offset), len);
2166                 } else {
2167                         /* We have to read regardless */
2168                         serv_read(ipc, aaa, len);
2169                         len = 0;
2170                 }
2171         }
2172         CtdlIPC_unlock(ipc);
2173         return len;
2174 }
2175
2176
2177 /* CLOS */
2178 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2179 {
2180         register int ret;
2181
2182         if (!cret) return -2;
2183         if (!ipc->downloading) return -2;
2184
2185         ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2186         if (ret / 100 == 2)
2187                 ipc->downloading = 0;
2188         return ret;
2189 }
2190
2191
2192 /* MSGP */
2193 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2194         register int ret;
2195         char cmd[SIZ];
2196         
2197         snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2198         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2199         return ret;
2200 }
2201
2202
2203
2204 /* READ */
2205 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2206                 void (*progress_gauge_callback)
2207                         (CtdlIPC*, unsigned long, unsigned long),
2208                char *cret)
2209 {
2210         register size_t len;
2211
2212         if (!cret) return -1;
2213         if (!buf) return -1;
2214         if (*buf) return -1;
2215         if (!ipc->downloading) return -1;
2216
2217         len = resume;
2218         if (progress_gauge_callback)
2219                 progress_gauge_callback(ipc, len, bytes);
2220         while (len < bytes) {
2221                 register size_t block;
2222
2223                 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2224                 if (block == 0) {
2225                         free(*buf);
2226                         return 0;
2227                 }
2228                 len += block;
2229                 if (progress_gauge_callback)
2230                         progress_gauge_callback(ipc, len, bytes);
2231         }
2232         return len;
2233 }
2234
2235 /* READ - pipelined */
2236 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2237                size_t resume,
2238                 void (*progress_gauge_callback)
2239                         (CtdlIPC*, unsigned long, unsigned long),
2240                char *cret)
2241 {
2242         register size_t len;
2243         register int calls;     /* How many calls in the pipeline */
2244         register int i;         /* iterator */
2245         char aaa[4096];
2246
2247         if (!cret) return -1;
2248         if (!buf) return -1;
2249         if (*buf) return -1;
2250         if (!ipc->downloading) return -1;
2251
2252         *buf = (void *)realloc(*buf, bytes - resume);
2253         if (!*buf) return -1;
2254
2255         len = 0;
2256         CtdlIPC_lock(ipc);
2257         if (progress_gauge_callback)
2258                 progress_gauge_callback(ipc, len, bytes);
2259
2260         /* How many calls will be in the pipeline? */
2261         calls = (bytes - resume) / 4096;
2262         if ((bytes - resume) % 4096) calls++;
2263
2264         /* Send all requests at once */
2265         for (i = 0; i < calls; i++) {
2266                 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2267                 CtdlIPC_putline(ipc, aaa);
2268         }
2269
2270         /* Receive all responses at once */
2271         for (i = 0; i < calls; i++) {
2272                 CtdlIPC_getline(ipc, aaa);
2273                 if (aaa[0] != '6')
2274                         strcpy(cret, &aaa[4]);
2275                 else {
2276                         len = extract_long(&aaa[4], 0);
2277                         /* I know what I'm doing */
2278                         serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2279                 }
2280                 if (progress_gauge_callback)
2281                         progress_gauge_callback(ipc, i * 4096 + len, bytes);
2282         }
2283         CtdlIPC_unlock(ipc);
2284         return len;
2285 }
2286
2287
2288 /* UCLS */
2289 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2290 {
2291         register int ret;
2292         char cmd[8];
2293
2294         if (!cret) return -1;
2295         if (!ipc->uploading) return -1;
2296
2297         sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2298         ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2299         ipc->uploading = 0;
2300         return ret;
2301 }
2302
2303
2304 /* WRIT */
2305 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2306                 void (*progress_gauge_callback)
2307                         (CtdlIPC*, unsigned long, unsigned long),
2308                 char *cret)
2309 {
2310         register int ret = -1;
2311         register size_t offset = 0;
2312         size_t bytes;
2313         char aaa[SIZ];
2314         char buf[4096];
2315         FILE *fd;
2316
2317         if (!cret) return -1;
2318         if (!path) return -1;
2319         if (!*path) return -1;
2320
2321         fd = fopen(path, "r");
2322         if (!fd) return -2;
2323
2324         fseek(fd, 0L, SEEK_END);
2325         bytes = ftell(fd);
2326         rewind(fd);
2327
2328         if (progress_gauge_callback)
2329                 progress_gauge_callback(ipc, 0, bytes);
2330
2331         while (offset < bytes) {
2332                 register size_t to_write;
2333
2334                 /* Read some data in */
2335                 to_write = fread(buf, 1, 4096, fd);
2336                 if (!to_write) {
2337                         if (feof(fd) || ferror(fd)) break;
2338                 }
2339                 sprintf(aaa, "WRIT %d", (int)to_write);
2340                 CtdlIPC_putline(ipc, aaa);
2341                 CtdlIPC_getline(ipc, aaa);
2342                 strcpy(cret, &aaa[4]);
2343                 ret = atoi(aaa);
2344                 if (aaa[0] == '7') {
2345                         to_write = extract_long(&aaa[4], 0);
2346                         
2347                         serv_write(ipc, buf, to_write);
2348                         offset += to_write;
2349                         if (progress_gauge_callback)
2350                                 progress_gauge_callback(ipc, offset, bytes);
2351                         /* Detect short reads and back up if needed */
2352                         /* offset will never be negative anyway */
2353                         fseek(fd, (signed)offset, SEEK_SET);
2354                 } else {
2355                         break;
2356                 }
2357         }
2358         if (progress_gauge_callback)
2359                 progress_gauge_callback(ipc, 1, 1);
2360         return (!ferror(fd) ? ret : -2);
2361 }
2362
2363
2364 /*
2365  * Generic command method.  This method should handle any server command
2366  * except for CHAT.  It takes the following arguments:
2367  *
2368  * ipc                  The server to speak with
2369  * command              Preformatted command to send to server
2370  * to_send              A text or binary file to send to server
2371  *                      (only sent if server requests it)
2372  * bytes_to_send        The number of bytes in to_send (required if
2373  *                      sending binary, optional if sending listing)
2374  * to_receive           Pointer to a NULL pointer, if the server
2375  *                      sends text or binary we will allocate memory
2376  *                      for the file and stuff it here
2377  * bytes_to_receive     If a file is received, we will store its
2378  *                      byte count here
2379  * proto_response       The protocol response.  Caller must provide
2380  *                      this buffer and ensure that it is at least
2381  *                      128 bytes in length.
2382  *
2383  * This function returns a number equal to the protocol response number,
2384  * -1 if an internal error occurred, -2 if caller provided bad values,
2385  * or 0 - the protocol response number if bad values were found during
2386  * the protocol exchange.
2387  * It stores the protocol response string (minus the number) in 
2388  * protocol_response as described above.  Some commands send additional
2389  * data in this string.
2390  */
2391 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2392                 const char *command, const char *to_send,
2393                 size_t bytes_to_send, char **to_receive, 
2394                 size_t *bytes_to_receive, char *proto_response)
2395 {
2396         char buf[SIZ];
2397         register int ret;
2398         int watch_ssl = 0;
2399
2400         if (!command) return -2;
2401         if (!proto_response) return -2;
2402
2403 #ifdef HAVE_OPENSSL
2404         if (ipc->ssl) watch_ssl = 1;
2405 #endif
2406
2407         CtdlIPC_lock(ipc);
2408         CtdlIPC_putline(ipc, command);
2409         while (1) {
2410                 CtdlIPC_getline(ipc, proto_response);
2411                 if (proto_response[3] == '*')
2412                         instant_msgs = 1;
2413                 ret = atoi(proto_response);
2414                 strcpy(proto_response, &proto_response[4]);
2415                 switch (ret / 100) {
2416                 default:                        /* Unknown, punt */
2417                 case 2:                         /* OK */
2418                 case 3:                         /* MORE_DATA */
2419                 case 5:                         /* ERROR */
2420                         /* Don't need to do anything */
2421                         break;
2422                 case 1:                         /* LISTING_FOLLOWS */
2423                         if (to_receive && !*to_receive && bytes_to_receive) {
2424                                 *to_receive = CtdlIPCReadListing(ipc, NULL);
2425                         } else { /* Drain */
2426                                 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2427                                 ret = -ret;
2428                         }
2429                         break;
2430                 case 4:                         /* SEND_LISTING */
2431                         if (to_send) {
2432                                 CtdlIPCSendListing(ipc, to_send);
2433                         } else {
2434                                 /* No listing given, fake it */
2435                                 CtdlIPC_putline(ipc, "000");
2436                                 ret = -ret;
2437                         }
2438                         break;
2439                 case 6:                         /* BINARY_FOLLOWS */
2440                         if (to_receive && !*to_receive && bytes_to_receive) {
2441                                 *bytes_to_receive =
2442                                         extract_long(proto_response, 0);
2443                                 *to_receive = (char *)
2444                                         malloc((size_t)*bytes_to_receive);
2445                                 if (!*to_receive) {
2446                                         ret = -1;
2447                                 } else {
2448                                         serv_read(ipc, *to_receive,
2449                                                         *bytes_to_receive);
2450                                 }
2451                         } else {
2452                                 /* Drain */
2453                                 size_t drain;
2454
2455                                 drain = extract_long(proto_response, 0);
2456                                 while (drain > SIZ) {
2457                                         serv_read(ipc, buf, SIZ);
2458                                         drain -= SIZ;
2459                                 }
2460                                 serv_read(ipc, buf, drain);
2461                                 ret = -ret;
2462                         }
2463                         break;
2464                 case 7:                         /* SEND_BINARY */
2465                         if (to_send && bytes_to_send) {
2466                                 serv_write(ipc, to_send, bytes_to_send);
2467                         } else if (bytes_to_send) {
2468                                 /* Fake it, send nulls */
2469                                 size_t fake;
2470
2471                                 fake = bytes_to_send;
2472                                 memset(buf, '\0', SIZ);
2473                                 while (fake > SIZ) {
2474                                         serv_write(ipc, buf, SIZ);
2475                                         fake -= SIZ;
2476                                 }
2477                                 serv_write(ipc, buf, fake);
2478                                 ret = -ret;
2479                         } /* else who knows?  DANGER WILL ROBINSON */
2480                         break;
2481                 case 8:                         /* START_CHAT_MODE */
2482                         if (!strncasecmp(command, "CHAT", 4)) {
2483                                 /* Don't call chatmode with generic! */
2484                                 CtdlIPC_putline(ipc, "/quit");
2485                                 ret = -ret;
2486                         } else {
2487                                 /* In this mode we send then receive listing */
2488                                 if (to_send) {
2489                                         CtdlIPCSendListing(ipc, to_send);
2490                                 } else {
2491                                         /* No listing given, fake it */
2492                                         CtdlIPC_putline(ipc, "000");
2493                                         ret = -ret;
2494                                 }
2495                                 if (to_receive && !*to_receive
2496                                                 && bytes_to_receive) {
2497                                         *to_receive = CtdlIPCReadListing(ipc, NULL);
2498                                 } else { /* Drain */
2499                                         while (CtdlIPC_getline(ipc, buf),
2500                                                         strcmp(buf, "000")) ;
2501                                         ret = -ret;
2502                                 }
2503                         }
2504                         break;
2505                 case 9:                         /* ASYNC_MSG */
2506                         /* CtdlIPCDoAsync(ret, proto_response); */
2507                         free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
2508                         break;
2509                 }
2510                 if (ret / 100 != 9)
2511                         break;
2512         }
2513         CtdlIPC_unlock(ipc);
2514         return ret;
2515 }
2516
2517
2518 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2519 {
2520         struct hostent *phe;
2521         struct servent *pse;
2522         struct protoent *ppe;
2523         struct sockaddr_in sin;
2524         int s, type;
2525
2526         memset(&sin, 0, sizeof(sin));
2527         sin.sin_family = AF_INET;
2528
2529         pse = getservbyname(service, protocol);
2530         if (pse != NULL) {
2531                 sin.sin_port = pse->s_port;
2532         }
2533         else if (atoi(service) > 0) {
2534                 sin.sin_port = htons(atoi(service));
2535         }
2536         else {
2537                 sin.sin_port = htons(defaultPort);
2538         }
2539         phe = gethostbyname(host);
2540         if (phe) {
2541                 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2542         } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2543                 return -1;
2544         }
2545         if ((ppe = getprotobyname(protocol)) == 0) {
2546                 return -1;
2547         }
2548         if (!strcmp(protocol, "udp")) {
2549                 type = SOCK_DGRAM;
2550         } else {
2551                 type = SOCK_STREAM;
2552         }
2553
2554         s = socket(PF_INET, type, ppe->p_proto);
2555         if (s < 0) {
2556                 return -1;
2557         }
2558
2559         if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2560                 close(s);
2561                 return -1;
2562         }
2563
2564         return (s);
2565 }
2566
2567 static int uds_connectsock(int *isLocal, char *sockpath)
2568 {
2569         struct sockaddr_un addr;
2570         int s;
2571
2572         memset(&addr, 0, sizeof(addr));
2573         addr.sun_family = AF_UNIX;
2574         safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2575
2576         s = socket(AF_UNIX, SOCK_STREAM, 0);
2577         if (s < 0) {
2578                 return -1;
2579         }
2580
2581         if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2582                 close(s);
2583                 return -1;
2584         }
2585
2586         *isLocal = 1;
2587         return s;
2588 }
2589
2590
2591 /*
2592  * input binary data from socket
2593  */
2594 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2595 {
2596         unsigned int len, rlen;
2597
2598 #if defined(HAVE_OPENSSL)
2599         if (ipc->ssl) {
2600                 serv_read_ssl(ipc, buf, bytes);
2601                 return;
2602         }
2603 #endif
2604         len = 0;
2605         while (len < bytes) {
2606                 rlen = read(ipc->sock, &buf[len], bytes - len);
2607                 if (rlen < 1) {
2608                         connection_died(ipc, 0);
2609                         return;
2610                 }
2611                 len += rlen;
2612         }
2613 }
2614
2615
2616 /*
2617  * send binary to server
2618  */
2619 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2620 {
2621         unsigned int bytes_written = 0;
2622         int retval;
2623
2624 #if defined(HAVE_OPENSSL)
2625         if (ipc->ssl) {
2626                 serv_write_ssl(ipc, buf, nbytes);
2627                 return;
2628         }
2629 #endif
2630         while (bytes_written < nbytes) {
2631                 retval = write(ipc->sock, &buf[bytes_written],
2632                                nbytes - bytes_written);
2633                 if (retval < 1) {
2634                         connection_died(ipc, 0);
2635                         return;
2636                 }
2637                 bytes_written += retval;
2638         }
2639 }
2640
2641
2642 #ifdef HAVE_OPENSSL
2643 /*
2644  * input binary data from encrypted connection
2645  */
2646 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2647 {
2648         int len, rlen;
2649         char junk[1];
2650
2651         len = 0;
2652         while (len < bytes) {
2653                 if (SSL_want_read(ipc->ssl)) {
2654                         if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2655                                 error_printf("SSL_write in serv_read:\n");
2656                                 ERR_print_errors_fp(stderr);
2657                         }
2658                 }
2659                 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2660                 if (rlen < 1) {
2661                         long errval;
2662
2663                         errval = SSL_get_error(ipc->ssl, rlen);
2664                         if (errval == SSL_ERROR_WANT_READ ||
2665                                         errval == SSL_ERROR_WANT_WRITE) {
2666                                 sleep(1);
2667                                 continue;
2668                         }
2669 /***
2670  Not sure why we'd want to handle these error codes any differently,
2671  but this definitely isn't the way to handle them.  Someone must have
2672  naively assumed that we could fall back to unencrypted communications,
2673  but all it does is just recursively blow the stack.
2674                         if (errval == SSL_ERROR_ZERO_RETURN ||
2675                                         errval == SSL_ERROR_SSL) {
2676                                 serv_read(ipc, &buf[len], bytes - len);
2677                                 return;
2678                         }
2679  ***/
2680                         error_printf("SSL_read in serv_read: %s\n",
2681                                         ERR_reason_error_string(ERR_peek_error()));
2682                         connection_died(ipc, 1);
2683                         return;
2684                 }
2685                 len += rlen;
2686         }
2687 }
2688
2689
2690 /*
2691  * send binary to server encrypted
2692  */
2693 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2694 {
2695         unsigned int bytes_written = 0;
2696         int retval;
2697         char junk[1];
2698
2699         while (bytes_written < nbytes) {
2700                 if (SSL_want_write(ipc->ssl)) {
2701                         if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2702                                 error_printf("SSL_read in serv_write:\n");
2703                                 ERR_print_errors_fp(stderr);
2704                         }
2705                 }
2706                 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2707                                 nbytes - bytes_written);
2708                 if (retval < 1) {
2709                         long errval;
2710
2711                         errval = SSL_get_error(ipc->ssl, retval);
2712                         if (errval == SSL_ERROR_WANT_READ ||
2713                                         errval == SSL_ERROR_WANT_WRITE) {
2714                                 sleep(1);
2715                                 continue;
2716                         }
2717                         if (errval == SSL_ERROR_ZERO_RETURN ||
2718                                         errval == SSL_ERROR_SSL) {
2719                                 serv_write(ipc, &buf[bytes_written],
2720                                                 nbytes - bytes_written);
2721                                 return;
2722                         }
2723                         error_printf("SSL_write in serv_write: %s\n",
2724                                         ERR_reason_error_string(ERR_peek_error()));
2725                         connection_died(ipc, 1);
2726                         return;
2727                 }
2728                 bytes_written += retval;
2729         }
2730 }
2731
2732
2733 static void CtdlIPC_init_OpenSSL(void)
2734 {
2735         int a;
2736         SSL_METHOD *ssl_method;
2737         DH *dh;
2738         
2739         /* already done init */
2740         if (ssl_ctx) {
2741                 return;
2742         }
2743
2744         /* Get started */
2745         a = 0;
2746         ssl_ctx = NULL;
2747         dh = NULL;
2748         SSL_load_error_strings();
2749         SSLeay_add_ssl_algorithms();
2750
2751         /* Set up the SSL context in which we will oeprate */
2752         ssl_method = SSLv23_client_method();
2753         ssl_ctx = SSL_CTX_new(ssl_method);
2754         if (!ssl_ctx) {
2755                 error_printf("SSL_CTX_new failed: %s\n",
2756                                 ERR_reason_error_string(ERR_get_error()));
2757                 return;
2758         }
2759         /* Any reasonable cipher we can get */
2760         if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2761                 error_printf("No ciphers available for encryption\n");
2762                 return;
2763         }
2764         SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2765         
2766         /* Load DH parameters into the context */
2767         dh = DH_new();
2768         if (!dh) {
2769                 error_printf("Can't allocate a DH object: %s\n",
2770                                 ERR_reason_error_string(ERR_get_error()));
2771                 return;
2772         }
2773         if (!(BN_hex2bn(&(dh->p), DH_P))) {
2774                 error_printf("Can't assign DH_P: %s\n",
2775                                 ERR_reason_error_string(ERR_get_error()));
2776                 DH_free(dh);
2777                 return;
2778         }
2779         if (!(BN_hex2bn(&(dh->g), DH_G))) {
2780                 error_printf("Can't assign DH_G: %s\n",
2781                                 ERR_reason_error_string(ERR_get_error()));
2782                 DH_free(dh);
2783                 return;
2784         }
2785         dh->length = DH_L;
2786         SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2787         DH_free(dh);
2788
2789 #ifdef THREADED_CLIENT
2790         /* OpenSSL requires callbacks for threaded clients */
2791         CRYPTO_set_locking_callback(ssl_lock);
2792         CRYPTO_set_id_callback(id_callback);
2793
2794         /* OpenSSL requires us to do semaphores for threaded clients */
2795         Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2796         if (!Critters) {
2797                 perror("malloc failed");
2798                 exit(1);
2799         } else {
2800                 for (a = 0; a < CRYPTO_num_locks(); a++) {
2801                         Critters[a] = malloc(sizeof (pthread_mutex_t));
2802                         if (!Critters[a]) {
2803                                 perror("malloc failed");
2804                                 exit(1);
2805                         }
2806                         pthread_mutex_init(Critters[a], NULL);
2807                 }
2808         }
2809 #endif /* THREADED_CLIENT */       
2810 }
2811
2812
2813 static void ssl_lock(int mode, int n, const char *file, int line)
2814 {
2815 #ifdef THREADED_CLIENT
2816         if (mode & CRYPTO_LOCK)
2817                 pthread_mutex_lock(Critters[n]);
2818         else
2819                 pthread_mutex_unlock(Critters[n]);
2820 #endif /* THREADED_CLIENT */
2821 }
2822
2823 #ifdef THREADED_CLIENT
2824 static unsigned long id_callback(void) {
2825         return (unsigned long)pthread_self();
2826 }
2827 #endif /* THREADED_CLIENT */
2828 #endif /* HAVE_OPENSSL */
2829
2830
2831 /*
2832  * input string from socket - implemented in terms of serv_read()
2833  */
2834 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2835 {
2836         int i;
2837
2838         /* Read one character at a time. */
2839         for (i = 0;; i++) {
2840                 serv_read(ipc, &buf[i], 1);
2841                 if (buf[i] == '\n' || i == (SIZ-1))
2842                         break;
2843         }
2844
2845         /* If we got a long line, discard characters until the newline. */
2846         if (i == (SIZ-1))
2847                 while (buf[i] != '\n')
2848                         serv_read(ipc, &buf[i], 1);
2849
2850         /* Strip the trailing newline (and carriage return, if present) */
2851         if (buf[i] == 10) buf[i--] = 0;
2852         if (buf[i] == 13) buf[i--] = 0;
2853 }
2854
2855 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2856 {
2857         CtdlIPC_getline(ipc, buf);
2858 }
2859
2860 /*
2861  * send line to server - implemented in terms of serv_write()
2862  */
2863 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2864 {
2865         char *cmd = NULL;
2866         int len;
2867
2868         len = strlen(buf);
2869         cmd = malloc(len + 2);
2870         if (!cmd) {
2871                 /* This requires no extra memory */
2872                 serv_write(ipc, buf, len);
2873                 serv_write(ipc, "\n", 1);
2874         } else {
2875                 /* This is network-optimized */
2876                 strncpy(cmd, buf, len);
2877                 strcpy(cmd + len, "\n");
2878                 serv_write(ipc, cmd, len + 1);
2879                 free(cmd);
2880         }
2881
2882         ipc->last_command_sent = time(NULL);
2883 }
2884
2885 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
2886 {
2887         CtdlIPC_putline(ipc, buf);
2888 }
2889
2890
2891 /*
2892  * attach to server
2893  */
2894 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2895 {
2896         int a;
2897         char cithost[SIZ];
2898         char citport[SIZ];
2899         char sockpath[SIZ];
2900         CtdlIPC* ipc;
2901
2902         ipc = ialloc(CtdlIPC);
2903         if (!ipc) {
2904                 return 0;
2905         }
2906 #if defined(HAVE_OPENSSL)
2907         ipc->ssl = NULL;
2908         CtdlIPC_init_OpenSSL();
2909 #endif
2910 #if defined(HAVE_PTHREAD_H)
2911         pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2912 #endif
2913         ipc->sock = -1;                 /* Not connected */
2914         ipc->isLocal = 0;               /* Not local, of course! */
2915         ipc->downloading = 0;
2916         ipc->uploading = 0;
2917         ipc->last_command_sent = 0L;
2918         ipc->network_status_cb = NULL;
2919
2920         strcpy(cithost, DEFAULT_HOST);  /* default host */
2921         strcpy(citport, DEFAULT_PORT);  /* default port */
2922
2923         /* Allow caller to supply our values (Windows) */
2924         if (hostbuf && strlen(hostbuf) > 0)
2925                 strcpy(cithost, hostbuf);
2926         if (portbuf && strlen(portbuf) > 0)
2927                 strcpy(citport, portbuf);
2928
2929         /* Read host/port from command line if present */
2930         for (a = 0; a < argc; ++a) {
2931                 if (a == 0) {
2932                         /* do nothing */
2933                 } else if (a == 1) {
2934                         strcpy(cithost, argv[a]);
2935                 } else if (a == 2) {
2936                         strcpy(citport, argv[a]);
2937                 } else {
2938                         error_printf("%s: usage: ",argv[0]);
2939                         error_printf("%s [host] [port] ",argv[0]);
2940                         ifree(ipc);
2941                         errno = EINVAL;
2942                         return 0;
2943                 }
2944         }
2945
2946         if ((!strcmp(cithost, "localhost"))
2947            || (!strcmp(cithost, "127.0.0.1"))) {
2948                 ipc->isLocal = 1;
2949         }
2950
2951         /* If we're using a unix domain socket we can do a bunch of stuff */
2952         if (!strcmp(cithost, UDS)) {
2953                 if (!strcasecmp(citport, DEFAULT_PORT)) {
2954                         snprintf(sockpath, sizeof sockpath, file_citadel_socket);
2955                 }
2956                 else {
2957                         snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
2958                 }
2959                 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2960                 if (ipc->sock == -1) {
2961                         ifree(ipc);
2962                         return 0;
2963                 }
2964                 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2965                 if (portbuf != NULL) strcpy(portbuf, sockpath);
2966                 return ipc;
2967         }
2968
2969         ipc->sock = connectsock(cithost, citport, "tcp", 504);
2970         if (ipc->sock == -1) {
2971                 ifree(ipc);
2972                 return 0;
2973         }
2974         if (hostbuf != NULL) strcpy(hostbuf, cithost);
2975         if (portbuf != NULL) strcpy(portbuf, citport);
2976         return ipc;
2977 }
2978
2979
2980 /*
2981  * Disconnect and delete the IPC class (destructor)
2982  */
2983 void CtdlIPC_delete(CtdlIPC* ipc)
2984 {
2985 #ifdef HAVE_OPENSSL
2986         if (ipc->ssl) {
2987                 SSL_shutdown(ipc->ssl);
2988                 SSL_free(ipc->ssl);
2989                 ipc->ssl = NULL;
2990         }
2991 #endif
2992         if (ipc->sock > -1) {
2993                 shutdown(ipc->sock, 2); /* Close it up */
2994                 ipc->sock = -1;
2995         }
2996         ifree(ipc);
2997 }
2998
2999
3000 /*
3001  * Disconnect and delete the IPC class (destructor)
3002  * Also NULLs out the pointer
3003  */
3004 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3005 {
3006         CtdlIPC_delete(*pipc);
3007         *pipc = NULL;
3008 }
3009
3010
3011 /*
3012  * return the file descriptor of the server socket so we can select() on it.
3013  *
3014  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3015  * rewritten...
3016  */
3017 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3018 {
3019         return ipc->sock;
3020 }
3021
3022
3023 /*
3024  * return one character
3025  *
3026  * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3027  * rewritten...
3028  */
3029 char CtdlIPC_get(CtdlIPC* ipc)
3030 {
3031         char buf[2];
3032         char ch;
3033
3034         serv_read(ipc, buf, 1);
3035         ch = (int) buf[0];
3036
3037         return (ch);
3038 }