* When saving a Task, if the status is COMPLETED then also set PERCENT-COMPLETE:100...
[citadel.git] / citadel / citadel_ipc.c
blob476780c60dc5e12753796a7492dd88e354c1d731
1 /* $Id$ */
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
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 */
49 #endif /* HAVE_OPENSSL */
51 #ifndef INADDR_NONE
52 #define INADDR_NONE 0xffffffff
53 #endif
55 static void (*status_hook)(char *s) = NULL;
57 void setCryptoStatusHook(void (*hook)(char *s)) {
58 status_hook = hook;
61 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
62 ipc->network_status_cb = hook;
66 char instant_msgs = 0;
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);
84 const char *svn_revision(void);
87 * Does nothing. The server should always return 200.
89 int CtdlIPCNoop(CtdlIPC *ipc)
91 char aaa[128];
93 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
98 * Does nothing interesting. The server should always return 200
99 * along with your string.
101 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
103 register int ret;
104 char *aaa;
106 if (!arg) return -2;
107 if (!cret) return -2;
109 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
110 if (!aaa) return -1;
112 sprintf(aaa, "ECHO %s", arg);
113 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
114 free(aaa);
115 return ret;
120 * Asks the server to close the connecction.
121 * Should always return 200.
123 int CtdlIPCQuit(CtdlIPC *ipc)
125 register int ret = 221; /* Default to successful quit */
126 char aaa[SIZ];
128 CtdlIPC_lock(ipc);
129 if (ipc->sock > -1) {
130 CtdlIPC_putline(ipc, "QUIT");
131 CtdlIPC_getline(ipc, aaa);
132 ret = atoi(aaa);
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;
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!
151 int CtdlIPCLogout(CtdlIPC *ipc)
153 register int ret;
154 char aaa[SIZ];
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;
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.
170 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
172 register int ret;
173 char *aaa;
175 if (!username) return -2;
176 if (!cret) return -2;
178 aaa = (char *)malloc((size_t)(strlen(username) + 6));
179 if (!aaa) return -1;
181 sprintf(aaa, "USER %s", username);
182 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
183 free(aaa);
184 return ret;
189 * Second stage of authentication - provide password. The server returns
190 * 200 and several arguments in cret relating to the user's account.
192 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
194 register int ret;
195 char *aaa;
197 if (!passwd) return -2;
198 if (!cret) return -2;
200 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
201 if (!aaa) return -1;
203 sprintf(aaa, "PASS %s", passwd);
204 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
205 free(aaa);
206 return ret;
211 * Second stage of authentication - provide password. The server returns
212 * 200 and several arguments in cret relating to the user's account.
214 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
216 register int ret;
217 char *aaa;
219 if (!response) return -2;
220 if (!cret) return -2;
222 aaa = (char *)malloc((size_t)(strlen(response) + 6));
223 if (!aaa) return -1;
225 sprintf(aaa, "PAS2 %s", response);
226 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
227 free(aaa);
228 return ret;
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.
239 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
241 register int ret;
242 char *aaa;
244 if (!username) return -2;
245 if (!cret) return -2;
247 aaa = (char *)malloc((size_t)(strlen(username) + 6));
248 if (!aaa) return -1;
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;
258 * Changes the user's password. Returns 200 if changed, errors otherwise.
260 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
262 register int ret;
263 char *aaa;
265 if (!passwd) return -2;
266 if (!cret) return -2;
268 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
269 if (!aaa) return -1;
271 sprintf(aaa, "SETP %s", passwd);
272 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
273 free(aaa);
274 return ret;
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)
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;
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 */
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;
303 while (bbb && strlen(bbb)) {
304 int a;
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;
323 mptr2 = march;
324 while (mptr2->next != NULL)
325 mptr2 = mptr2->next;
326 mptr2->next = mptr;
331 *listing = march;
332 if (bbb) free(bbb);
333 return ret;
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)
341 register int ret;
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;
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);
354 return ret;
358 /* SETU */
359 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
361 char aaa[48];
363 if (!uret) return -2;
364 if (!cret) return -2;
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);
373 /* RENU */
374 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
376 register int ret;
377 char cmd[256];
379 if (!oldname) return -2;
380 if (!newname) return -2;
381 if (!cret) return -2;
383 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
384 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
385 return ret;
389 /* GOTO */
390 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
391 struct ctdlipcroom **rret, char *cret)
393 register int ret;
394 char *aaa;
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;
401 if (passwd) {
402 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
403 if (!aaa) {
404 free(*rret);
405 return -1;
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;
414 sprintf(aaa, "GOTO %s", room);
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;
434 free(aaa);
435 return ret;
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)
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;
453 if (!cret) return -2;
454 if (!mret) return -2;
455 if (*mret) return -2;
456 if (which < 0 || which > 6) return -2;
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;
484 if (bbb) free(bbb);
485 return ret;
489 /* MSG0, MSG2 */
490 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
491 struct ctdlipcmessage **mret, char *cret)
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];
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;
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');
518 if (!strncasecmp(aaa, "nhdr=yes", 8))
519 mret[0]->nhdr = 1;
520 else if (!strncasecmp(aaa, "from=", 5))
521 safestrncpy(mret[0]->author, &aaa[5], SIZ);
522 else if (!strncasecmp(aaa, "type=", 5))
523 mret[0]->type = atoi(&aaa[5]);
524 else if (!strncasecmp(aaa, "msgn=", 5))
525 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
526 else if (!strncasecmp(aaa, "subj=", 5))
527 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
528 else if (!strncasecmp(aaa, "rfca=", 5))
529 safestrncpy(mret[0]->email, &aaa[5], SIZ);
530 else if (!strncasecmp(aaa, "hnod=", 5))
531 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
532 else if (!strncasecmp(aaa, "room=", 5))
533 safestrncpy(mret[0]->room, &aaa[5], SIZ);
534 else if (!strncasecmp(aaa, "node=", 5))
535 safestrncpy(mret[0]->node, &aaa[5], SIZ);
536 else if (!strncasecmp(aaa, "rcpt=", 5))
537 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
538 else if (!strncasecmp(aaa, "wefw=", 5))
539 safestrncpy(mret[0]->references, &aaa[5], SIZ);
540 else if (!strncasecmp(aaa, "time=", 5))
541 mret[0]->time = atol(&aaa[5]);
543 /* Multipart/alternative prefix & suffix strings help
544 * us to determine which part we want to download.
546 else if (!strncasecmp(aaa, "pref=", 5)) {
547 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
548 if (!strcasecmp(multipart_prefix,
549 "multipart/alternative")) {
550 ++multipart_hunting;
553 else if (!strncasecmp(aaa, "suff=", 5)) {
554 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
555 if (!strcasecmp(multipart_prefix,
556 "multipart/alternative")) {
557 ++multipart_hunting;
561 else if (!strncasecmp(aaa, "part=", 5)) {
562 struct parts *ptr, *chain;
564 ptr = (struct parts *)calloc(1, sizeof (struct parts));
565 if (ptr) {
567 /* Fill the buffers for the caller */
568 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
569 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
570 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
571 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
572 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
573 ptr->length = extract_long(&aaa[5], 5);
574 if (!mret[0]->attachments)
575 mret[0]->attachments = ptr;
576 else {
577 chain = mret[0]->attachments;
578 while (chain->next)
579 chain = chain->next;
580 chain->next = ptr;
583 /* Now handle multipart/alternative */
584 if (multipart_hunting > 0) {
585 if ( (!strcasecmp(ptr->mimetype,
586 "text/plain"))
587 || (!strcasecmp(ptr->mimetype,
588 "text/html")) ) {
589 strcpy(mret[0]->mime_chosen,
590 ptr->number);
597 /* Eliminate "text\n" */
598 remove_token(bbb, 0, '\n');
600 /* If doing a MIME thing, pull out the extra headers */
601 if (as_mime == 4) {
602 do {
603 if (!strncasecmp(bbb, "Content-type:", 13)) {
604 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
605 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
606 striplt(mret[0]->content_type);
608 /* strip out ";charset=" portion. FIXME do something with
609 * the charset (like... convert it) instead of just throwing
610 * it away
612 if (strstr(mret[0]->content_type, ";") != NULL) {
613 strcpy(strstr(mret[0]->content_type, ";"), "");
617 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
618 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
619 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
620 striplt(mret[0]->mime_chosen);
622 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
623 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
624 strcpy(encoding, &encoding[26]);
625 striplt(encoding);
627 remove_token(bbb, 0, '\n');
628 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
629 remove_token(bbb, 0, '\n');
634 if (strlen(bbb)) {
636 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
637 char *ccc = NULL;
638 int bytes_decoded = 0;
639 ccc = malloc(strlen(bbb) + 32768);
640 if (!strcasecmp(encoding, "base64")) {
641 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
643 else if (!strcasecmp(encoding, "quoted-printable")) {
644 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
646 ccc[bytes_decoded] = 0;
647 free(bbb);
648 bbb = ccc;
651 /* FIXME: Strip trailing whitespace */
652 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
654 } else {
655 bbb = (char *)realloc(bbb, 1);
656 *bbb = '\0';
658 mret[0]->text = bbb;
660 return ret;
664 /* WHOK */
665 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
667 register int ret;
668 size_t bytes;
670 if (!cret) return -2;
671 if (!listing) return -2;
672 if (*listing) return -2;
674 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
675 return ret;
679 /* INFO */
680 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
682 register int ret;
683 size_t bytes;
684 char *listing = NULL;
685 char buf[SIZ];
687 if (!cret) return -2;
689 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
690 if (ret / 100 == 1) {
691 int line = 0;
693 while (*listing && strlen(listing)) {
694 extract_token(buf, listing, 0, '\n', sizeof buf);
695 remove_token(listing, 0, '\n');
696 switch (line++) {
697 case 0: ipc->ServInfo.pid = atoi(buf);
698 break;
699 case 1: strcpy(ipc->ServInfo.nodename,buf);
700 break;
701 case 2: strcpy(ipc->ServInfo.humannode,buf);
702 break;
703 case 3: strcpy(ipc->ServInfo.fqdn,buf);
704 break;
705 case 4: strcpy(ipc->ServInfo.software,buf);
706 break;
707 case 5: ipc->ServInfo.rev_level = atoi(buf);
708 break;
709 case 6: strcpy(ipc->ServInfo.site_location,buf);
710 break;
711 case 7: strcpy(ipc->ServInfo.sysadm,buf);
712 break;
713 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
714 break;
715 case 10: ipc->ServInfo.ok_floors = atoi(buf);
716 break;
717 case 11: ipc->ServInfo.paging_level = atoi(buf);
718 break;
719 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
720 break;
721 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
722 break;
723 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
724 break;
725 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
726 break;
727 case 17: ipc->ServInfo.load_avg = atof(buf);
728 break;
729 case 18: ipc->ServInfo.worker_avg = atof(buf);
730 break;
731 case 19: ipc->ServInfo.thread_count = atoi(buf);
732 break;
733 case 20: ipc->ServInfo.has_sieve = atoi(buf);
734 break;
735 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
736 break;
737 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
738 break;
743 if (listing) free(listing);
744 return ret;
748 /* RDIR */
749 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
751 register int ret;
752 size_t bytes;
754 if (!cret) return -2;
755 if (!listing) return -2;
756 if (*listing) return -2;
758 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
759 return ret;
764 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
766 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
768 register int ret;
769 char aaa[16];
771 if (!cret) return -2;
773 if (msgnum) {
774 sprintf(aaa, "SLRP %ld", msgnum);
776 else {
777 sprintf(aaa, "SLRP HIGHEST");
779 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
780 return ret;
784 /* INVT */
785 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
787 register int ret;
788 char *aaa;
790 if (!cret) return -2;
791 if (!username) return -2;
793 aaa = (char *)malloc(strlen(username) + 6);
794 if (!aaa) return -1;
796 sprintf(aaa, "INVT %s", username);
797 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
798 free(aaa);
799 return ret;
803 /* KICK */
804 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
806 register int ret;
807 char *aaa;
809 if (!cret) return -1;
810 if (!username) return -1;
812 aaa = (char *)malloc(strlen(username) + 6);
814 sprintf(aaa, "KICK %s", username);
815 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
816 free(aaa);
817 return ret;
821 /* GETR */
822 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
824 register int ret;
826 if (!cret) return -2;
827 if (!qret) return -2;
828 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
829 if (!*qret) return -1;
831 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
832 if (ret / 100 == 2) {
833 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
834 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
835 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
836 qret[0]->QRflags = extract_int(cret, 3);
837 qret[0]->QRfloor = extract_int(cret, 4);
838 qret[0]->QRorder = extract_int(cret, 5);
839 qret[0]->QRdefaultview = extract_int(cret, 6);
840 qret[0]->QRflags2 = extract_int(cret, 7);
842 return ret;
846 /* SETR */
847 /* set forget to kick all users out of room */
848 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
850 register int ret;
851 char *aaa;
853 if (!cret) return -2;
854 if (!qret) return -2;
856 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
857 strlen(qret->QRdirname) + 64);
858 if (!aaa) return -1;
860 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
861 qret->QRname, qret->QRpasswd, qret->QRdirname,
862 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
863 qret->QRdefaultview, qret->QRflags2);
864 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
865 free(aaa);
866 return ret;
870 /* GETA */
871 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
873 if (!cret) return -1;
875 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
879 /* SETA */
880 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
882 register int ret;
883 char *aaa;
885 if (!cret) return -2;
886 if (!username) return -2;
888 aaa = (char *)malloc(strlen(username) + 6);
889 if (!aaa) return -1;
891 sprintf(aaa, "SETA %s", username);
892 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
893 free(aaa);
894 return ret;
898 /* ENT0 */
899 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
901 register int ret;
902 char cmd[SIZ];
903 char *ptr;
905 if (!cret) return -2;
906 if (!mr) return -2;
908 if (mr->references) {
909 for (ptr=mr->references; *ptr != 0; ++ptr) {
910 if (*ptr == '|') *ptr = '!';
914 snprintf(cmd, sizeof cmd,
915 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
916 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
917 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
918 NULL, cret);
919 if ((flag == 0) && (subject_required != NULL)) {
920 /* Is the server strongly recommending that the user enter a message subject? */
921 if ((cret[3] != '\0') && (cret[4] != '\0')) {
922 *subject_required = extract_int(&cret[4], 1);
927 return ret;
931 /* RINF */
932 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
934 size_t bytes;
936 if (!cret) return -2;
937 if (!iret) return -2;
938 if (*iret) return -2;
940 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
944 /* DELE */
945 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
947 char aaa[16];
949 if (!cret) return -2;
950 if (!msgnum) return -2;
952 sprintf(aaa, "DELE %ld", msgnum);
953 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
957 /* MOVE */
958 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
960 register int ret;
961 char *aaa;
963 if (!cret) return -2;
964 if (!destroom) return -2;
965 if (!msgnum) return -2;
967 aaa = (char *)malloc(strlen(destroom) + 28);
968 if (!aaa) return -1;
970 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
971 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
972 free(aaa);
973 return ret;
977 /* KILL */
978 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
980 char aaa[16];
982 if (!cret) return -2;
984 sprintf(aaa, "KILL %d", for_real);
985 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
989 /* CRE8 */
990 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
991 const char *password, int floor, char *cret)
993 register int ret;
994 char *aaa;
996 if (!cret) return -2;
997 if (!roomname) return -2;
999 if (password) {
1000 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
1001 if (!aaa) return -1;
1002 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
1003 password, floor);
1004 } else {
1005 aaa = (char *)malloc(strlen(roomname) + 40);
1006 if (!aaa) return -1;
1007 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1008 floor);
1010 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1011 free(aaa);
1012 return ret;
1016 /* FORG */
1017 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1019 if (!cret) return -2;
1021 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1025 /* MESG */
1026 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1028 register int ret;
1029 char *aaa;
1030 size_t bytes;
1032 if (!cret) return -2;
1033 if (!mret) return -2;
1034 if (*mret) return -2;
1035 if (!message) return -2;
1037 aaa = (char *)malloc(strlen(message) + 6);
1038 if (!aaa) return -1;
1040 sprintf(aaa, "MESG %s", message);
1041 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1042 free(aaa);
1043 return ret;
1047 /* GNUR */
1048 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1050 if (!cret) return -2;
1052 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1056 /* GREG */
1057 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1059 register int ret;
1060 char *aaa;
1061 size_t bytes;
1063 if (!cret) return -2;
1064 if (!rret) return -2;
1065 if (*rret) return -2;
1067 if (username)
1068 aaa = (char *)malloc(strlen(username) + 6);
1069 else
1070 aaa = (char *)malloc(12);
1071 if (!aaa) return -1;
1073 if (username)
1074 sprintf(aaa, "GREG %s", username);
1075 else
1076 sprintf(aaa, "GREG _SELF_");
1077 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1078 free(aaa);
1079 return ret;
1083 /* VALI */
1084 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1086 register int ret;
1087 char *aaa;
1089 if (!cret) return -2;
1090 if (!username) return -2;
1091 if (axlevel < 0 || axlevel > 7) return -2;
1093 aaa = (char *)malloc(strlen(username) + 17);
1094 if (!aaa) return -1;
1096 sprintf(aaa, "VALI %s|%d", username, axlevel);
1097 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1098 free(aaa);
1099 return ret;
1103 /* EINF */
1104 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1106 char aaa[16];
1108 if (!cret) return -1;
1109 if (!info) return -1;
1111 sprintf(aaa, "EINF %d", for_real);
1112 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1116 /* LIST */
1117 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1119 size_t bytes;
1120 char *cmd;
1121 int ret;
1123 if (!cret) return -1;
1124 if (!listing) return -1;
1125 if (*listing) return -1;
1126 if (!searchstring) return -1;
1128 cmd = malloc(strlen(searchstring) + 10);
1129 sprintf(cmd, "LIST %s", searchstring);
1131 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1132 free(cmd);
1133 return(ret);
1137 /* REGI */
1138 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1140 if (!cret) return -1;
1141 if (!info) return -1;
1143 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1144 NULL, NULL, cret);
1148 /* CHEK */
1149 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1151 register int ret;
1153 if (!cret) return -1;
1154 if (!chek) return -1;
1156 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1157 if (ret / 100 == 2) {
1158 chek->newmail = extract_long(cret, 0);
1159 chek->needregis = extract_int(cret, 1);
1160 chek->needvalid = extract_int(cret, 2);
1162 return ret;
1166 /* DELF */
1167 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1169 register int ret;
1170 char *aaa;
1172 if (!cret) return -2;
1173 if (!filename) return -2;
1175 aaa = (char *)malloc(strlen(filename) + 6);
1176 if (!aaa) return -1;
1178 sprintf(aaa, "DELF %s", filename);
1179 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1180 free(aaa);
1181 return ret;
1185 /* MOVF */
1186 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1188 register int ret;
1189 char *aaa;
1191 if (!cret) return -2;
1192 if (!filename) return -2;
1193 if (!destroom) return -2;
1195 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1196 if (!aaa) return -1;
1198 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1199 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1200 free(aaa);
1201 return ret;
1205 /* RWHO */
1206 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1208 register int ret;
1209 size_t bytes;
1211 if (!cret) return -1;
1212 if (!listing) return -1;
1213 if (*listing) return -1;
1215 *stamp = CtdlIPCServerTime(ipc, cret);
1216 if (!*stamp)
1217 *stamp = time(NULL);
1218 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1219 return ret;
1223 /* OPEN */
1224 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1225 size_t resume,
1226 void (*progress_gauge_callback)
1227 (CtdlIPC*, unsigned long, unsigned long),
1228 char *cret)
1230 register int ret;
1231 size_t bytes;
1232 time_t last_mod;
1233 char mimetype[SIZ];
1234 char *aaa;
1236 if (!cret) return -2;
1237 if (!filename) return -2;
1238 if (!buf) return -2;
1239 if (*buf) return -2;
1240 if (ipc->downloading) return -2;
1242 aaa = (char *)malloc(strlen(filename) + 6);
1243 if (!aaa) return -1;
1245 sprintf(aaa, "OPEN %s", filename);
1246 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1247 free(aaa);
1248 if (ret / 100 == 2) {
1249 ipc->downloading = 1;
1250 bytes = extract_long(cret, 0);
1251 last_mod = extract_int(cret, 1);
1252 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1254 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1255 progress_gauge_callback, cret);
1257 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1258 progress_gauge_callback, cret);
1261 ret = CtdlIPCEndDownload(ipc, cret);
1262 if (ret / 100 == 2)
1263 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1264 filename, mimetype);
1266 return ret;
1270 /* OPNA */
1271 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1272 void **buf,
1273 void (*progress_gauge_callback)
1274 (CtdlIPC*, unsigned long, unsigned long),
1275 char *cret)
1277 register int ret;
1278 size_t bytes;
1279 time_t last_mod;
1280 char filename[SIZ];
1281 char mimetype[SIZ];
1282 char aaa[SIZ];
1284 if (!cret) return -2;
1285 if (!buf) return -2;
1286 if (*buf) return -2;
1287 if (!part) return -2;
1288 if (!msgnum) return -2;
1289 if (ipc->downloading) return -2;
1291 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1292 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1293 if (ret / 100 == 2) {
1294 ipc->downloading = 1;
1295 bytes = extract_long(cret, 0);
1296 last_mod = extract_int(cret, 1);
1297 extract_token(filename, cret, 2, '|', sizeof filename);
1298 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1299 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1300 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1301 ret = CtdlIPCEndDownload(ipc, cret);
1302 if (ret / 100 == 2)
1303 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1304 filename, mimetype);
1306 return ret;
1310 /* OIMG */
1311 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1312 void (*progress_gauge_callback)
1313 (CtdlIPC*, unsigned long, unsigned long),
1314 char *cret)
1316 register int ret;
1317 size_t bytes;
1318 time_t last_mod;
1319 char mimetype[SIZ];
1320 char *aaa;
1322 if (!cret) return -1;
1323 if (!buf) return -1;
1324 if (*buf) return -1;
1325 if (!filename) return -1;
1326 if (ipc->downloading) return -1;
1328 aaa = (char *)malloc(strlen(filename) + 6);
1329 if (!aaa) return -1;
1331 sprintf(aaa, "OIMG %s", filename);
1332 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1333 free(aaa);
1334 if (ret / 100 == 2) {
1335 ipc->downloading = 1;
1336 bytes = extract_long(cret, 0);
1337 last_mod = extract_int(cret, 1);
1338 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1339 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1340 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1341 ret = CtdlIPCEndDownload(ipc, cret);
1342 if (ret / 100 == 2)
1343 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1344 filename, mimetype);
1346 return ret;
1350 /* UOPN */
1351 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1352 const char *path,
1353 void (*progress_gauge_callback)
1354 (CtdlIPC*, unsigned long, unsigned long),
1355 char *cret)
1357 register int ret;
1358 char *aaa;
1359 FILE *uploadFP;
1360 char MimeTestBuf[64];
1361 const char *MimeType;
1362 long len;
1364 if (!cret) return -1;
1365 if (!save_as) return -1;
1366 if (!comment) return -1;
1367 if (!path) return -1;
1368 if (!*path) return -1;
1369 if (ipc->uploading) return -1;
1371 uploadFP = fopen(path, "r");
1372 if (!uploadFP) return -2;
1374 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1375 rewind (uploadFP);
1376 if (len < 0)
1377 return -3;
1379 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1380 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1381 if (!aaa) return -1;
1383 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1384 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1385 free(aaa);
1386 if (ret / 100 == 2) {
1387 ipc->uploading = 1;
1388 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1389 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1390 ipc->uploading = 0;
1392 return ret;
1396 /* UIMG */
1397 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1398 const char *save_as,
1399 void (*progress_gauge_callback)
1400 (CtdlIPC*, unsigned long, unsigned long),
1401 char *cret)
1403 register int ret;
1404 FILE *uploadFP;
1405 char *aaa;
1406 char MimeTestBuf[64];
1407 const char *MimeType;
1408 long len;
1410 if (!cret) return -1;
1411 if (!save_as) return -1;
1412 if (!path && for_real) return -1;
1413 if (!*path && for_real) return -1;
1414 if (ipc->uploading) return -1;
1416 aaa = (char *)malloc(strlen(save_as) + 17);
1417 if (!aaa) return -1;
1419 uploadFP = fopen(path, "r");
1420 if (!uploadFP) return -2;
1422 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1423 rewind (uploadFP);
1424 if (len < 0)
1425 return -3;
1426 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1428 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1429 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1430 free(aaa);
1431 if (ret / 100 == 2 && for_real) {
1432 ipc->uploading = 1;
1433 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1434 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1435 ipc->uploading = 0;
1437 return ret;
1441 /* QUSR */
1442 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1444 register int ret;
1445 char *aaa;
1447 if (!cret) return -2;
1448 if (!username) return -2;
1450 aaa = (char *)malloc(strlen(username) + 6);
1451 if (!aaa) return -1;
1453 sprintf(aaa, "QUSR %s", username);
1454 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1455 free(aaa);
1456 return ret;
1460 /* LFLR */
1461 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1463 size_t bytes;
1465 if (!cret) return -2;
1466 if (!listing) return -2;
1467 if (*listing) return -2;
1469 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1473 /* CFLR */
1474 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1476 register int ret;
1477 char aaa[SIZ];
1479 if (!cret) return -2;
1480 if (!name) return -2;
1482 sprintf(aaa, "CFLR %s|%d", name, for_real);
1483 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1484 return ret;
1488 /* KFLR */
1489 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1491 char aaa[SIZ];
1493 if (!cret) return -1;
1494 if (floornum < 0) return -1;
1496 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1497 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1501 /* EFLR */
1502 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1504 register int ret;
1505 char aaa[SIZ];
1507 if (!cret) return -2;
1508 if (!floorname) return -2;
1509 if (floornum < 0) return -2;
1511 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1512 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1513 return ret;
1518 * IDEN
1520 * You only need to fill out hostname, the defaults will be used if any of the
1521 * other fields are not set properly.
1523 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1524 int revision, const char *software_name, const char *hostname,
1525 char *cret)
1527 register int ret;
1528 char *aaa;
1530 if (developerid < 0 || clientid < 0 || revision < 0 ||
1531 !software_name) {
1532 developerid = 8;
1533 clientid = 0;
1534 revision = REV_LEVEL - 600;
1535 software_name = "Citadel (libcitadel)";
1537 if (!hostname) return -2;
1539 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1540 if (!aaa) return -1;
1542 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1543 revision, software_name, hostname);
1544 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1545 free(aaa);
1546 return ret;
1550 /* SEXP */
1551 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1552 char *cret)
1554 register int ret;
1555 char *aaa;
1557 if (!cret) return -2;
1558 if (!username) return -2;
1560 aaa = (char *)malloc(strlen(username) + 8);
1561 if (!aaa) return -1;
1563 if (text) {
1564 sprintf(aaa, "SEXP %s|-", username);
1565 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1566 NULL, NULL, cret);
1567 } else {
1568 sprintf(aaa, "SEXP %s||", username);
1569 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1571 free(aaa);
1572 return ret;
1576 /* GEXP */
1577 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1579 size_t bytes;
1581 if (!cret) return -2;
1582 if (!listing) return -2;
1583 if (*listing) return -2;
1585 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1589 /* DEXP */
1590 /* mode is 0 = enable, 1 = disable, 2 = status */
1591 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1593 char aaa[16];
1595 if (!cret) return -2;
1597 sprintf(aaa, "DEXP %d", mode);
1598 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1602 /* EBIO */
1603 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1605 if (!cret) return -2;
1606 if (!bio) return -2;
1608 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1609 NULL, NULL, cret);
1613 /* RBIO */
1614 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1616 register int ret;
1617 size_t bytes;
1618 char *aaa;
1620 if (!cret) return -2;
1621 if (!username) return -2;
1622 if (!listing) return -2;
1623 if (*listing) return -2;
1625 aaa = (char *)malloc(strlen(username) + 6);
1626 if (!aaa) return -1;
1628 sprintf(aaa, "RBIO %s", username);
1629 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1630 free(aaa);
1631 return ret;
1635 /* LBIO */
1636 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1638 size_t bytes;
1640 if (!cret) return -2;
1641 if (!listing) return -2;
1642 if (*listing) return -2;
1644 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1648 /* STEL */
1649 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1651 char aaa[16];
1653 if (!cret) return -1;
1655 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1656 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1660 /* TERM */
1661 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1663 char aaa[16];
1665 if (!cret) return -1;
1667 sprintf(aaa, "TERM %d", sid);
1668 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1672 /* DOWN */
1673 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1675 if (!cret) return -1;
1677 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1681 /* SCDN */
1682 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1684 char aaa[16];
1686 if (!cret) return -1;
1688 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1689 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1693 /* EMSG */
1694 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1695 char *cret)
1697 register int ret;
1698 char *aaa;
1700 if (!cret) return -2;
1701 if (!text) return -2;
1702 if (!filename) return -2;
1704 aaa = (char *)malloc(strlen(filename) + 6);
1705 if (!aaa) return -1;
1707 sprintf(aaa, "EMSG %s", filename);
1708 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1709 free(aaa);
1710 return ret;
1714 /* HCHG */
1715 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1717 register int ret;
1718 char *aaa;
1720 if (!cret) return -2;
1721 if (!hostname) return -2;
1723 aaa = (char *)malloc(strlen(hostname) + 6);
1724 if (!aaa) return -1;
1726 sprintf(aaa, "HCHG %s", hostname);
1727 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1728 free(aaa);
1729 return ret;
1733 /* RCHG */
1734 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1736 register int ret;
1737 char *aaa;
1739 if (!cret) return -2;
1740 if (!roomname) return -2;
1742 aaa = (char *)malloc(strlen(roomname) + 6);
1743 if (!aaa) return -1;
1745 sprintf(aaa, "RCHG %s", roomname);
1746 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1747 free(aaa);
1748 return ret;
1752 /* UCHG */
1753 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1755 register int ret;
1756 char *aaa;
1758 if (!cret) return -2;
1759 if (!username) return -2;
1761 aaa = (char *)malloc(strlen(username) + 6);
1762 if (!aaa) return -1;
1764 sprintf(aaa, "UCHG %s", username);
1765 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1766 free(aaa);
1767 return ret;
1771 /* TIME */
1772 /* This function returns the actual server time reported, or 0 if error */
1773 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1775 register time_t tret;
1776 register int ret;
1778 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1779 if (ret / 100 == 2) {
1780 tret = extract_long(cret, 0);
1781 } else {
1782 tret = 0L;
1784 return tret;
1788 /* AGUP */
1789 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1790 struct ctdluser **uret, char *cret)
1792 register int ret;
1793 char aaa[SIZ];
1795 if (!cret) return -2;
1796 if (!uret) return -2;
1797 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1798 if (!*uret) return -1;
1800 sprintf(aaa, "AGUP %s", who);
1801 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1803 if (ret / 100 == 2) {
1804 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1805 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1806 uret[0]->flags = extract_int(cret, 2);
1807 uret[0]->timescalled = extract_long(cret, 3);
1808 uret[0]->posted = extract_long(cret, 4);
1809 uret[0]->axlevel = extract_int(cret, 5);
1810 uret[0]->usernum = extract_long(cret, 6);
1811 uret[0]->lastcall = extract_long(cret, 7);
1812 uret[0]->USuserpurge = extract_int(cret, 8);
1814 return ret;
1818 /* ASUP */
1819 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1821 register int ret;
1822 char *aaa;
1824 if (!cret) return -2;
1825 if (!uret) return -2;
1827 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1828 if (!aaa) return -1;
1830 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1831 uret->fullname, uret->password, uret->flags,
1832 uret->timescalled, uret->posted, uret->axlevel,
1833 uret->usernum, uret->lastcall, uret->USuserpurge);
1834 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1835 free(aaa);
1836 return ret;
1840 /* GPEX */
1841 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1842 /* caller must free the struct ExpirePolicy */
1843 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1844 struct ExpirePolicy **policy, char *cret)
1846 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1847 char cmd[256];
1848 register int ret;
1850 if (!cret) return -2;
1851 if (!policy) return -2;
1852 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1853 if (!*policy) return -1;
1854 if (which < 0 || which > 3) return -2;
1856 sprintf(cmd, "GPEX %s", proto[which]);
1857 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1858 if (ret / 100 == 2) {
1859 policy[0]->expire_mode = extract_int(cret, 0);
1860 policy[0]->expire_value = extract_int(cret, 1);
1862 return ret;
1866 /* SPEX */
1867 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1868 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1869 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1870 struct ExpirePolicy *policy, char *cret)
1872 char aaa[38];
1873 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1875 if (!cret) return -2;
1876 if (which < 0 || which > 3) return -2;
1877 if (!policy) return -2;
1878 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1879 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1881 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1882 policy->expire_mode, policy->expire_value);
1883 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1887 /* CONF GET */
1888 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1890 size_t bytes;
1892 if (!cret) return -2;
1893 if (!listing) return -2;
1894 if (*listing) return -2;
1896 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1897 listing, &bytes, cret);
1901 /* CONF SET */
1902 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1904 if (!cret) return -2;
1905 if (!listing) return -2;
1907 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1908 NULL, NULL, cret);
1912 /* CONF GETSYS */
1913 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1914 char **listing, char *cret)
1916 register int ret;
1917 char *aaa;
1918 size_t bytes;
1920 if (!cret) return -2;
1921 if (!mimetype) return -2;
1922 if (!listing) return -2;
1923 if (*listing) return -2;
1925 aaa = malloc(strlen(mimetype) + 13);
1926 if (!aaa) return -1;
1927 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1928 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1929 listing, &bytes, cret);
1930 free(aaa);
1931 return ret;
1935 /* CONF PUTSYS */
1936 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1937 const char *listing, char *cret)
1939 register int ret;
1940 char *aaa;
1942 if (!cret) return -2;
1943 if (!mimetype) return -2;
1944 if (!listing) return -2;
1946 aaa = malloc(strlen(mimetype) + 13);
1947 if (!aaa) return -1;
1948 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1949 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1950 NULL, NULL, cret);
1951 free(aaa);
1952 return ret;
1956 /* GNET */
1957 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1959 size_t bytes;
1961 if (!cret) return -2;
1962 if (!listing) return -2;
1963 if (*listing) return -2;
1965 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1966 listing, &bytes, cret);
1970 /* SNET */
1971 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1973 if (!cret) return -2;
1974 if (!listing) return -2;
1976 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1977 NULL, NULL, cret);
1981 /* REQT */
1982 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1984 char aaa[16];
1986 if (!cret) return -2;
1987 if (session < 0) return -2;
1989 sprintf(aaa, "REQT %d", session);
1990 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1994 /* SEEN */
1995 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1997 char aaa[27];
1999 if (!cret) return -2;
2000 if (msgnum < 0) return -2;
2002 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2003 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2007 /* STLS */
2008 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2010 int a;
2011 int r;
2012 char buf[SIZ];
2014 #ifdef HAVE_OPENSSL
2015 SSL *temp_ssl;
2017 /* New SSL object */
2018 temp_ssl = SSL_new(ssl_ctx);
2019 if (!temp_ssl) {
2020 error_printf("SSL_new failed: %s\n",
2021 ERR_reason_error_string(ERR_get_error()));
2022 return -2;
2024 /* Pointless flag waving */
2025 #if SSLEAY_VERSION_NUMBER >= 0x0922
2026 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
2027 #endif
2029 if (!access(EGD_POOL, F_OK))
2030 RAND_egd(EGD_POOL);
2032 if (!RAND_status()) {
2033 error_printf("PRNG not properly seeded\n");
2034 return -2;
2037 /* Associate network connection with SSL object */
2038 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2039 error_printf("SSL_set_fd failed: %s\n",
2040 ERR_reason_error_string(ERR_get_error()));
2041 return -2;
2044 if (status_hook != NULL)
2045 status_hook("Requesting encryption...\r");
2047 /* Ready to start SSL/TLS */
2048 /* Old code
2049 CtdlIPC_putline(ipc, "STLS");
2050 CtdlIPC_getline(ipc, buf);
2051 if (buf[0] != '2') {
2052 error_printf("Server can't start TLS: %s\n", buf);
2053 return 0;
2056 r = CtdlIPCGenericCommand(ipc,
2057 "STLS", NULL, 0, NULL, NULL, cret);
2058 if (r / 100 != 2) {
2059 error_printf("Server can't start TLS: %s\n", buf);
2060 endtls(temp_ssl);
2061 return r;
2064 /* Do SSL/TLS handshake */
2065 if ((a = SSL_connect(temp_ssl)) < 1) {
2066 error_printf("SSL_connect failed: %s\n",
2067 ERR_reason_error_string(ERR_get_error()));
2068 endtls(temp_ssl);
2069 return -2;
2071 ipc->ssl = temp_ssl;
2073 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2075 int bits, alg_bits;
2077 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2078 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2079 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2080 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2081 bits, alg_bits);
2083 return r;
2084 #else
2085 return 0;
2086 #endif /* HAVE_OPENSSL */
2090 #ifdef HAVE_OPENSSL
2091 static void endtls(SSL *ssl)
2093 if (ssl) {
2094 SSL_shutdown(ssl);
2095 SSL_free(ssl);
2098 #endif
2101 /* QDIR */
2102 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2104 register int ret;
2105 char *aaa;
2107 if (!address) return -2;
2108 if (!cret) return -2;
2110 aaa = (char *)malloc(strlen(address) + 6);
2111 if (!aaa) return -1;
2113 sprintf(aaa, "QDIR %s", address);
2114 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2115 free(aaa);
2116 return ret;
2120 /* IPGM */
2121 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2123 char aaa[30];
2125 if (!cret) return -2;
2126 sprintf(aaa, "IPGM %d", secret);
2127 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2131 /* FSCK */
2132 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2134 size_t size = 0;
2136 if (!cret) return -2;
2137 if (!mret) return -2;
2138 if (*mret) return -2;
2140 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2145 * Not implemented:
2147 * CHAT
2148 * ETLS
2149 * EXPI
2150 * GTLS
2151 * IGAB
2152 * MSG3
2153 * MSG4
2154 * NDOP
2155 * NETP
2156 * NUOP
2157 * SMTP
2161 /* ************************************************************************** */
2162 /* Stuff below this line is not for public consumption */
2163 /* ************************************************************************** */
2166 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2168 if (ipc->network_status_cb) ipc->network_status_cb(1);
2169 #ifdef THREADED_CLIENT
2170 pthread_mutex_lock(&(ipc->mutex));
2171 #endif
2175 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2177 #ifdef THREADED_CLIENT
2178 pthread_mutex_unlock(&(ipc->mutex));
2179 #endif
2180 if (ipc->network_status_cb) ipc->network_status_cb(0);
2184 /* Read a listing from the server up to 000. Append to dest if it exists */
2185 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2187 size_t length = 0;
2188 size_t linelength;
2189 char *ret = NULL;
2190 char aaa[SIZ];
2192 ret = dest;
2193 if (ret != NULL) {
2194 length = strlen(ret);
2195 } else {
2196 length = 0;
2199 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2200 linelength = strlen(aaa);
2201 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2202 if (ret) {
2203 strcpy(&ret[length], aaa);
2204 length += linelength;
2205 strcpy(&ret[length++], "\n");
2209 return(ret);
2213 /* Send a listing to the server; generate the ending 000. */
2214 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2216 char *text;
2218 text = (char *)malloc(strlen(listing) + 6);
2219 if (text) {
2220 strcpy(text, listing);
2221 while (text[strlen(text) - 1] == '\n')
2222 text[strlen(text) - 1] = '\0';
2223 strcat(text, "\n000");
2224 CtdlIPC_putline(ipc, text);
2225 free(text);
2226 text = NULL;
2227 } else {
2228 /* Malloc failed but we are committed to send */
2229 /* This may result in extra blanks at the bottom */
2230 CtdlIPC_putline(ipc, text);
2231 CtdlIPC_putline(ipc, "000");
2233 return 0;
2237 /* Partial read of file from server */
2238 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2240 register size_t len = 0;
2241 char aaa[SIZ];
2243 if (!buf) return 0;
2244 if (!cret) return 0;
2245 if (bytes < 1) return 0;
2247 CtdlIPC_lock(ipc);
2248 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2249 CtdlIPC_putline(ipc, aaa);
2250 CtdlIPC_getline(ipc, aaa);
2251 if (aaa[0] != '6')
2252 strcpy(cret, &aaa[4]);
2253 else {
2254 len = extract_long(&aaa[4], 0);
2255 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2256 if (*buf) {
2257 /* I know what I'm doing */
2258 serv_read(ipc, ((char *)(*buf) + offset), len);
2259 } else {
2260 /* We have to read regardless */
2261 serv_read(ipc, aaa, len);
2262 len = 0;
2265 CtdlIPC_unlock(ipc);
2266 return len;
2270 /* CLOS */
2271 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2273 register int ret;
2275 if (!cret) return -2;
2276 if (!ipc->downloading) return -2;
2278 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2279 if (ret / 100 == 2)
2280 ipc->downloading = 0;
2281 return ret;
2285 /* MSGP */
2286 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2287 register int ret;
2288 char cmd[SIZ];
2290 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2291 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2292 return ret;
2297 /* READ */
2298 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2299 void (*progress_gauge_callback)
2300 (CtdlIPC*, unsigned long, unsigned long),
2301 char *cret)
2303 register size_t len;
2305 if (!cret) return -1;
2306 if (!buf) return -1;
2307 if (*buf) return -1;
2308 if (!ipc->downloading) return -1;
2310 len = resume;
2311 if (progress_gauge_callback)
2312 progress_gauge_callback(ipc, len, bytes);
2313 while (len < bytes) {
2314 register size_t block;
2316 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2317 if (block == 0) {
2318 free(*buf);
2319 return 0;
2321 len += block;
2322 if (progress_gauge_callback)
2323 progress_gauge_callback(ipc, len, bytes);
2325 return len;
2328 /* READ - pipelined */
2329 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2330 size_t resume,
2331 void (*progress_gauge_callback)
2332 (CtdlIPC*, unsigned long, unsigned long),
2333 char *cret)
2335 register size_t len;
2336 register int calls; /* How many calls in the pipeline */
2337 register int i; /* iterator */
2338 char aaa[4096];
2340 if (!cret) return -1;
2341 if (!buf) return -1;
2342 if (*buf) return -1;
2343 if (!ipc->downloading) return -1;
2345 *buf = (void *)realloc(*buf, bytes - resume);
2346 if (!*buf) return -1;
2348 len = 0;
2349 CtdlIPC_lock(ipc);
2350 if (progress_gauge_callback)
2351 progress_gauge_callback(ipc, len, bytes);
2353 /* How many calls will be in the pipeline? */
2354 calls = (bytes - resume) / 4096;
2355 if ((bytes - resume) % 4096) calls++;
2357 /* Send all requests at once */
2358 for (i = 0; i < calls; i++) {
2359 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2360 CtdlIPC_putline(ipc, aaa);
2363 /* Receive all responses at once */
2364 for (i = 0; i < calls; i++) {
2365 CtdlIPC_getline(ipc, aaa);
2366 if (aaa[0] != '6')
2367 strcpy(cret, &aaa[4]);
2368 else {
2369 len = extract_long(&aaa[4], 0);
2370 /* I know what I'm doing */
2371 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2373 if (progress_gauge_callback)
2374 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2376 CtdlIPC_unlock(ipc);
2377 return len;
2381 /* UCLS */
2382 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2384 register int ret;
2385 char cmd[8];
2387 if (!cret) return -1;
2388 if (!ipc->uploading) return -1;
2390 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2391 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2392 ipc->uploading = 0;
2393 return ret;
2397 /* WRIT */
2398 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2399 void (*progress_gauge_callback)
2400 (CtdlIPC*, unsigned long, unsigned long),
2401 char *cret)
2403 register int ret = -1;
2404 register size_t offset = 0;
2405 size_t bytes;
2406 char aaa[SIZ];
2407 char buf[4096];
2408 FILE *fd = uploadFP;
2409 int ferr;
2411 if (!cret) return -1;
2413 fseek(fd, 0L, SEEK_END);
2414 bytes = ftell(fd);
2415 rewind(fd);
2417 if (progress_gauge_callback)
2418 progress_gauge_callback(ipc, 0, bytes);
2420 while (offset < bytes) {
2421 register size_t to_write;
2423 /* Read some data in */
2424 to_write = fread(buf, 1, 4096, fd);
2425 if (!to_write) {
2426 if (feof(fd) || ferror(fd)) break;
2428 sprintf(aaa, "WRIT %d", (int)to_write);
2429 CtdlIPC_putline(ipc, aaa);
2430 CtdlIPC_getline(ipc, aaa);
2431 strcpy(cret, &aaa[4]);
2432 ret = atoi(aaa);
2433 if (aaa[0] == '7') {
2434 to_write = extract_long(&aaa[4], 0);
2436 serv_write(ipc, buf, to_write);
2437 offset += to_write;
2438 if (progress_gauge_callback)
2439 progress_gauge_callback(ipc, offset, bytes);
2440 /* Detect short reads and back up if needed */
2441 /* offset will never be negative anyway */
2442 fseek(fd, (signed)offset, SEEK_SET);
2443 } else {
2444 break;
2447 if (progress_gauge_callback)
2448 progress_gauge_callback(ipc, 1, 1);
2449 ferr = ferror(fd);
2450 fclose(fd);
2451 return (!ferr ? ret : -2);
2456 * Generic command method. This method should handle any server command
2457 * except for CHAT. It takes the following arguments:
2459 * ipc The server to speak with
2460 * command Preformatted command to send to server
2461 * to_send A text or binary file to send to server
2462 * (only sent if server requests it)
2463 * bytes_to_send The number of bytes in to_send (required if
2464 * sending binary, optional if sending listing)
2465 * to_receive Pointer to a NULL pointer, if the server
2466 * sends text or binary we will allocate memory
2467 * for the file and stuff it here
2468 * bytes_to_receive If a file is received, we will store its
2469 * byte count here
2470 * proto_response The protocol response. Caller must provide
2471 * this buffer and ensure that it is at least
2472 * 128 bytes in length.
2474 * This function returns a number equal to the protocol response number,
2475 * -1 if an internal error occurred, -2 if caller provided bad values,
2476 * or 0 - the protocol response number if bad values were found during
2477 * the protocol exchange.
2478 * It stores the protocol response string (minus the number) in
2479 * protocol_response as described above. Some commands send additional
2480 * data in this string.
2482 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2483 const char *command, const char *to_send,
2484 size_t bytes_to_send, char **to_receive,
2485 size_t *bytes_to_receive, char *proto_response)
2487 char buf[SIZ];
2488 register int ret;
2489 int watch_ssl = 0;
2491 if (!command) return -2;
2492 if (!proto_response) return -2;
2494 #ifdef HAVE_OPENSSL
2495 if (ipc->ssl) watch_ssl = 1;
2496 #endif
2498 CtdlIPC_lock(ipc);
2499 CtdlIPC_putline(ipc, command);
2500 while (1) {
2501 CtdlIPC_getline(ipc, proto_response);
2502 if (proto_response[3] == '*')
2503 instant_msgs = 1;
2504 ret = atoi(proto_response);
2505 strcpy(proto_response, &proto_response[4]);
2506 switch (ret / 100) {
2507 default: /* Unknown, punt */
2508 case 2: /* OK */
2509 case 3: /* MORE_DATA */
2510 case 5: /* ERROR */
2511 /* Don't need to do anything */
2512 break;
2513 case 1: /* LISTING_FOLLOWS */
2514 if (to_receive && !*to_receive && bytes_to_receive) {
2515 *to_receive = CtdlIPCReadListing(ipc, NULL);
2516 } else { /* Drain */
2517 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2518 ret = -ret;
2520 break;
2521 case 4: /* SEND_LISTING */
2522 if (to_send) {
2523 CtdlIPCSendListing(ipc, to_send);
2524 } else {
2525 /* No listing given, fake it */
2526 CtdlIPC_putline(ipc, "000");
2527 ret = -ret;
2529 break;
2530 case 6: /* BINARY_FOLLOWS */
2531 if (to_receive && !*to_receive && bytes_to_receive) {
2532 *bytes_to_receive =
2533 extract_long(proto_response, 0);
2534 *to_receive = (char *)
2535 malloc((size_t)*bytes_to_receive);
2536 if (!*to_receive) {
2537 ret = -1;
2538 } else {
2539 serv_read(ipc, *to_receive,
2540 *bytes_to_receive);
2542 } else {
2543 /* Drain */
2544 size_t drain;
2546 drain = extract_long(proto_response, 0);
2547 while (drain > SIZ) {
2548 serv_read(ipc, buf, SIZ);
2549 drain -= SIZ;
2551 serv_read(ipc, buf, drain);
2552 ret = -ret;
2554 break;
2555 case 7: /* SEND_BINARY */
2556 if (to_send && bytes_to_send) {
2557 serv_write(ipc, to_send, bytes_to_send);
2558 } else if (bytes_to_send) {
2559 /* Fake it, send nulls */
2560 size_t fake;
2562 fake = bytes_to_send;
2563 memset(buf, '\0', SIZ);
2564 while (fake > SIZ) {
2565 serv_write(ipc, buf, SIZ);
2566 fake -= SIZ;
2568 serv_write(ipc, buf, fake);
2569 ret = -ret;
2570 } /* else who knows? DANGER WILL ROBINSON */
2571 break;
2572 case 8: /* START_CHAT_MODE */
2573 if (!strncasecmp(command, "CHAT", 4)) {
2574 /* Don't call chatmode with generic! */
2575 CtdlIPC_putline(ipc, "/quit");
2576 ret = -ret;
2577 } else {
2578 /* In this mode we send then receive listing */
2579 if (to_send) {
2580 CtdlIPCSendListing(ipc, to_send);
2581 } else {
2582 /* No listing given, fake it */
2583 CtdlIPC_putline(ipc, "000");
2584 ret = -ret;
2586 if (to_receive && !*to_receive
2587 && bytes_to_receive) {
2588 *to_receive = CtdlIPCReadListing(ipc, NULL);
2589 } else { /* Drain */
2590 while (CtdlIPC_getline(ipc, buf),
2591 strcmp(buf, "000")) ;
2592 ret = -ret;
2595 break;
2596 case 9: /* ASYNC_MSG */
2597 /* CtdlIPCDoAsync(ret, proto_response); */
2598 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2599 break;
2601 if (ret / 100 != 9)
2602 break;
2604 CtdlIPC_unlock(ipc);
2605 return ret;
2609 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2611 struct hostent *phe;
2612 struct servent *pse;
2613 struct protoent *ppe;
2614 struct sockaddr_in sin;
2615 int s, type;
2617 memset(&sin, 0, sizeof(sin));
2618 sin.sin_family = AF_INET;
2620 pse = getservbyname(service, protocol);
2621 if (pse != NULL) {
2622 sin.sin_port = pse->s_port;
2624 else if (atoi(service) > 0) {
2625 sin.sin_port = htons(atoi(service));
2627 else {
2628 sin.sin_port = htons(defaultPort);
2630 phe = gethostbyname(host);
2631 if (phe) {
2632 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2633 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2634 return -1;
2636 if ((ppe = getprotobyname(protocol)) == 0) {
2637 return -1;
2639 if (!strcmp(protocol, "udp")) {
2640 type = SOCK_DGRAM;
2641 } else {
2642 type = SOCK_STREAM;
2645 s = socket(PF_INET, type, ppe->p_proto);
2646 if (s < 0) {
2647 return -1;
2650 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2651 close(s);
2652 return -1;
2655 return (s);
2658 static int uds_connectsock(int *isLocal, char *sockpath)
2660 struct sockaddr_un addr;
2661 int s;
2663 memset(&addr, 0, sizeof(addr));
2664 addr.sun_family = AF_UNIX;
2665 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2667 s = socket(AF_UNIX, SOCK_STREAM, 0);
2668 if (s < 0) {
2669 return -1;
2672 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2673 close(s);
2674 return -1;
2677 *isLocal = 1;
2678 return s;
2683 * input binary data from socket
2685 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2687 unsigned int len, rlen;
2689 #if defined(HAVE_OPENSSL)
2690 if (ipc->ssl) {
2691 serv_read_ssl(ipc, buf, bytes);
2692 return;
2694 #endif
2695 len = 0;
2696 while (len < bytes) {
2697 rlen = read(ipc->sock, &buf[len], bytes - len);
2698 if (rlen < 1) {
2699 connection_died(ipc, 0);
2700 return;
2702 len += rlen;
2708 * send binary to server
2710 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2712 unsigned int bytes_written = 0;
2713 int retval;
2715 #if defined(HAVE_OPENSSL)
2716 if (ipc->ssl) {
2717 serv_write_ssl(ipc, buf, nbytes);
2718 return;
2720 #endif
2721 while (bytes_written < nbytes) {
2722 retval = write(ipc->sock, &buf[bytes_written],
2723 nbytes - bytes_written);
2724 if (retval < 1) {
2725 connection_died(ipc, 0);
2726 return;
2728 bytes_written += retval;
2733 #ifdef HAVE_OPENSSL
2735 * input binary data from encrypted connection
2737 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2739 int len, rlen;
2740 char junk[1];
2742 len = 0;
2743 while (len < bytes) {
2744 if (SSL_want_read(ipc->ssl)) {
2745 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2746 error_printf("SSL_write in serv_read:\n");
2747 ERR_print_errors_fp(stderr);
2750 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2751 if (rlen < 1) {
2752 long errval;
2754 errval = SSL_get_error(ipc->ssl, rlen);
2755 if (errval == SSL_ERROR_WANT_READ ||
2756 errval == SSL_ERROR_WANT_WRITE) {
2757 sleep(1);
2758 continue;
2760 /***
2761 Not sure why we'd want to handle these error codes any differently,
2762 but this definitely isn't the way to handle them. Someone must have
2763 naively assumed that we could fall back to unencrypted communications,
2764 but all it does is just recursively blow the stack.
2765 if (errval == SSL_ERROR_ZERO_RETURN ||
2766 errval == SSL_ERROR_SSL) {
2767 serv_read(ipc, &buf[len], bytes - len);
2768 return;
2770 ***/
2771 error_printf("SSL_read in serv_read: %s\n",
2772 ERR_reason_error_string(ERR_peek_error()));
2773 connection_died(ipc, 1);
2774 return;
2776 len += rlen;
2782 * send binary to server encrypted
2784 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2786 unsigned int bytes_written = 0;
2787 int retval;
2788 char junk[1];
2790 while (bytes_written < nbytes) {
2791 if (SSL_want_write(ipc->ssl)) {
2792 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2793 error_printf("SSL_read in serv_write:\n");
2794 ERR_print_errors_fp(stderr);
2797 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2798 nbytes - bytes_written);
2799 if (retval < 1) {
2800 long errval;
2802 errval = SSL_get_error(ipc->ssl, retval);
2803 if (errval == SSL_ERROR_WANT_READ ||
2804 errval == SSL_ERROR_WANT_WRITE) {
2805 sleep(1);
2806 continue;
2808 if (errval == SSL_ERROR_ZERO_RETURN ||
2809 errval == SSL_ERROR_SSL) {
2810 serv_write(ipc, &buf[bytes_written],
2811 nbytes - bytes_written);
2812 return;
2814 error_printf("SSL_write in serv_write: %s\n",
2815 ERR_reason_error_string(ERR_peek_error()));
2816 connection_died(ipc, 1);
2817 return;
2819 bytes_written += retval;
2824 #ifdef THREADED_CLIENT
2825 static void ssl_lock(int mode, int n, const char *file, int line)
2827 if (mode & CRYPTO_LOCK)
2828 pthread_mutex_lock(Critters[n]);
2829 else
2830 pthread_mutex_unlock(Critters[n]);
2832 #endif /* THREADED_CLIENT */
2835 static void CtdlIPC_init_OpenSSL(void)
2837 int a;
2838 SSL_METHOD *ssl_method;
2839 DH *dh;
2841 /* already done init */
2842 if (ssl_ctx) {
2843 return;
2846 /* Get started */
2847 a = 0;
2848 ssl_ctx = NULL;
2849 dh = NULL;
2850 SSL_load_error_strings();
2851 SSLeay_add_ssl_algorithms();
2853 /* Set up the SSL context in which we will oeprate */
2854 ssl_method = SSLv23_client_method();
2855 ssl_ctx = SSL_CTX_new(ssl_method);
2856 if (!ssl_ctx) {
2857 error_printf("SSL_CTX_new failed: %s\n",
2858 ERR_reason_error_string(ERR_get_error()));
2859 return;
2861 /* Any reasonable cipher we can get */
2862 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2863 error_printf("No ciphers available for encryption\n");
2864 return;
2866 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2868 /* Load DH parameters into the context */
2869 dh = DH_new();
2870 if (!dh) {
2871 error_printf("Can't allocate a DH object: %s\n",
2872 ERR_reason_error_string(ERR_get_error()));
2873 return;
2875 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2876 error_printf("Can't assign DH_P: %s\n",
2877 ERR_reason_error_string(ERR_get_error()));
2878 DH_free(dh);
2879 return;
2881 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2882 error_printf("Can't assign DH_G: %s\n",
2883 ERR_reason_error_string(ERR_get_error()));
2884 DH_free(dh);
2885 return;
2887 dh->length = DH_L;
2888 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2889 DH_free(dh);
2891 #ifdef THREADED_CLIENT
2892 /* OpenSSL requires callbacks for threaded clients */
2893 CRYPTO_set_locking_callback(ssl_lock);
2894 CRYPTO_set_id_callback(id_callback);
2896 /* OpenSSL requires us to do semaphores for threaded clients */
2897 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2898 if (!Critters) {
2899 perror("malloc failed");
2900 exit(1);
2901 } else {
2902 for (a = 0; a < CRYPTO_num_locks(); a++) {
2903 Critters[a] = malloc(sizeof (pthread_mutex_t));
2904 if (!Critters[a]) {
2905 perror("malloc failed");
2906 exit(1);
2908 pthread_mutex_init(Critters[a], NULL);
2911 #endif /* THREADED_CLIENT */
2916 #ifdef THREADED_CLIENT
2917 static unsigned long id_callback(void) {
2918 return (unsigned long)pthread_self();
2920 #endif /* THREADED_CLIENT */
2921 #endif /* HAVE_OPENSSL */
2925 ReadNetworkChunk(CtdlIPC* ipc)
2927 fd_set read_fd;
2928 int tries;
2929 int ret = 0;
2930 int err = 0;
2931 struct timeval tv;
2932 size_t n;
2934 tv.tv_sec = 1;
2935 tv.tv_usec = 1000;
2936 tries = 0;
2937 n = 0;
2938 while (1)
2940 errno=0;
2941 FD_ZERO(&read_fd);
2942 FD_SET(ipc->sock, &read_fd);
2943 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2945 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2947 if (ret > 0) {
2949 *(ipc->BufPtr) = '\0';
2950 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2951 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2952 if (n > 0) {
2953 ipc->BufPtr[n]='\0';
2954 ipc->BufUsed += n;
2955 return n;
2957 else
2958 return n;
2960 else if (ret < 0) {
2961 if (!(errno == EINTR || errno == EAGAIN))
2962 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2963 return -1;
2965 else {
2966 tries ++;
2967 if (tries >= 10)
2968 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2969 if (n > 0) {
2970 ipc->BufPtr[n]='\0';
2971 ipc->BufUsed += n;
2972 return n;
2974 else {
2975 connection_died(ipc, 0);
2976 return -1;
2983 * input string from socket - implemented in terms of serv_read()
2985 #ifdef CHUNKED_READ
2987 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2989 int i, ntries;
2990 char *aptr, *bptr, *aeptr, *beptr;
2992 // error_printf("---\n");
2994 beptr = buf + SIZ;
2995 #if defined(HAVE_OPENSSL)
2996 if (ipc->ssl) {
2998 /* Read one character at a time. */
2999 for (i = 0;; i++) {
3000 serv_read(ipc, &buf[i], 1);
3001 if (buf[i] == '\n' || i == (SIZ-1))
3002 break;
3005 /* If we got a long line, discard characters until the newline. */
3006 if (i == (SIZ-1))
3007 while (buf[i] != '\n')
3008 serv_read(ipc, &buf[i], 1);
3010 /* Strip the trailing newline (and carriage return, if present) */
3011 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3012 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3014 else
3015 #endif
3017 if (ipc->Buf == NULL)
3019 ipc->BufSize = SIZ;
3020 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3021 *(ipc->Buf) = '\0';
3022 ipc->BufPtr = ipc->Buf;
3025 ntries = 0;
3026 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3027 if (ipc->BufUsed == 0)
3028 ReadNetworkChunk(ipc);
3030 //// if (ipc->BufUsed != 0) while (1)
3031 bptr = buf;
3033 while (1)
3035 aptr = ipc->BufPtr;
3036 aeptr = ipc->Buf + ipc->BufSize;
3037 while ((aptr < aeptr) &&
3038 (bptr < beptr) &&
3039 (*aptr != '\0') &&
3040 (*aptr != '\n'))
3041 *(bptr++) = *(aptr++);
3042 if ((*aptr == '\n') && (aptr < aeptr))
3044 /* Terminate it right, remove the line breaks */
3045 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3046 aptr ++;
3047 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3048 aptr ++;
3049 *(bptr++) = '\0';
3050 // 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);
3051 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3052 *(--bptr) = '\0';
3054 /* is there more in the buffer we need to read later? */
3055 if (ipc->Buf + ipc->BufUsed > aptr)
3057 ipc->BufPtr = aptr;
3059 else
3061 ipc->BufUsed = 0;
3062 ipc->BufPtr = ipc->Buf;
3064 // error_printf("----bla6\n");
3065 return;
3067 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3068 else if ((ipc->BufPtr != ipc->Buf) &&
3069 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3071 size_t NewBufSize = ipc->BufSize * 2;
3072 int delta = (ipc->BufPtr - ipc->Buf);
3073 char *NewBuf;
3075 /* if the line would end after our buffer, we should use a bigger buffer. */
3076 NewBuf = (char *)malloc (NewBufSize + 10);
3077 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3078 free(ipc->Buf);
3079 ipc->Buf = ipc->BufPtr = NewBuf;
3080 ipc->BufUsed -= delta;
3081 ipc->BufSize = NewBufSize;
3083 if (ReadNetworkChunk(ipc) <0)
3085 // error_printf("----bla\n");
3086 return;
3089 /// error_printf("----bl45761%s\nipc->BufUsed");
3091 // error_printf("----bla1\n");
3094 #else /* CHUNKED_READ */
3096 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3098 int i;
3100 /* Read one character at a time. */
3101 for (i = 0;; i++) {
3102 serv_read(ipc, &buf[i], 1);
3103 if (buf[i] == '\n' || i == (SIZ-1))
3104 break;
3107 /* If we got a long line, discard characters until the newline. */
3108 if (i == (SIZ-1))
3109 while (buf[i] != '\n')
3110 serv_read(ipc, &buf[i], 1);
3112 /* Strip the trailing newline (and carriage return, if present) */
3113 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3114 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3118 #endif /* CHUNKED_READ */
3121 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3123 CtdlIPC_getline(ipc, buf);
3127 * send line to server - implemented in terms of serv_write()
3129 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3131 char *cmd = NULL;
3132 int len;
3134 len = strlen(buf);
3135 cmd = malloc(len + 2);
3136 if (!cmd) {
3137 /* This requires no extra memory */
3138 serv_write(ipc, buf, len);
3139 serv_write(ipc, "\n", 1);
3140 } else {
3141 /* This is network-optimized */
3142 strncpy(cmd, buf, len);
3143 strcpy(cmd + len, "\n");
3144 serv_write(ipc, cmd, len + 1);
3145 free(cmd);
3148 ipc->last_command_sent = time(NULL);
3151 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3153 CtdlIPC_putline(ipc, buf);
3158 * attach to server
3160 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3162 int a;
3163 char cithost[SIZ];
3164 char citport[SIZ];
3165 char sockpath[SIZ];
3166 CtdlIPC* ipc;
3168 ipc = ialloc(CtdlIPC);
3169 if (!ipc) {
3170 return 0;
3172 #if defined(HAVE_OPENSSL)
3173 ipc->ssl = NULL;
3174 CtdlIPC_init_OpenSSL();
3175 #endif
3176 #if defined(HAVE_PTHREAD_H)
3177 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3178 #endif
3179 ipc->sock = -1; /* Not connected */
3180 ipc->isLocal = 0; /* Not local, of course! */
3181 ipc->downloading = 0;
3182 ipc->uploading = 0;
3183 ipc->last_command_sent = 0L;
3184 ipc->network_status_cb = NULL;
3185 ipc->Buf = NULL;
3186 ipc->BufUsed = 0;
3187 ipc->BufPtr = NULL;
3189 strcpy(cithost, DEFAULT_HOST); /* default host */
3190 strcpy(citport, DEFAULT_PORT); /* default port */
3192 /* Allow caller to supply our values (Windows) */
3193 if (hostbuf && strlen(hostbuf) > 0)
3194 strcpy(cithost, hostbuf);
3195 if (portbuf && strlen(portbuf) > 0)
3196 strcpy(citport, portbuf);
3198 /* Read host/port from command line if present */
3199 for (a = 0; a < argc; ++a) {
3200 if (a == 0) {
3201 /* do nothing */
3202 } else if (a == 1) {
3203 strcpy(cithost, argv[a]);
3204 } else if (a == 2) {
3205 strcpy(citport, argv[a]);
3206 } else {
3207 error_printf("%s: usage: ",argv[0]);
3208 error_printf("%s [host] [port] ",argv[0]);
3209 ifree(ipc);
3210 errno = EINVAL;
3211 return 0;
3215 if ((!strcmp(cithost, "localhost"))
3216 || (!strcmp(cithost, "127.0.0.1"))) {
3217 ipc->isLocal = 1;
3220 /* If we're using a unix domain socket we can do a bunch of stuff */
3221 if (!strcmp(cithost, UDS)) {
3222 if (!strcasecmp(citport, DEFAULT_PORT)) {
3223 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3225 else {
3226 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3228 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3229 if (ipc->sock == -1) {
3230 ifree(ipc);
3231 return 0;
3233 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3234 if (portbuf != NULL) strcpy(portbuf, sockpath);
3235 return ipc;
3238 ipc->sock = connectsock(cithost, citport, "tcp", 504);
3239 if (ipc->sock == -1) {
3240 ifree(ipc);
3241 return 0;
3243 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3244 if (portbuf != NULL) strcpy(portbuf, citport);
3245 return ipc;
3250 * Disconnect and delete the IPC class (destructor)
3252 void CtdlIPC_delete(CtdlIPC* ipc)
3254 #ifdef HAVE_OPENSSL
3255 if (ipc->ssl) {
3256 SSL_shutdown(ipc->ssl);
3257 SSL_free(ipc->ssl);
3258 ipc->ssl = NULL;
3260 #endif
3261 if (ipc->sock > -1) {
3262 shutdown(ipc->sock, 2); /* Close it up */
3263 ipc->sock = -1;
3265 if (ipc->Buf != NULL)
3266 free (ipc->Buf);
3267 ipc->Buf = NULL;
3268 ipc->BufPtr = NULL;
3269 ifree(ipc);
3274 * Disconnect and delete the IPC class (destructor)
3275 * Also NULLs out the pointer
3277 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3279 CtdlIPC_delete(*pipc);
3280 *pipc = NULL;
3285 * return the file descriptor of the server socket so we can select() on it.
3287 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3288 * rewritten...
3290 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3292 return ipc->sock;
3297 * return one character
3299 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3300 * rewritten...
3302 char CtdlIPC_get(CtdlIPC* ipc)
3304 char buf[2];
3305 char ch;
3307 serv_read(ipc, buf, 1);
3308 ch = (int) buf[0];
3310 return (ch);