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