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