3 * Copyright (c) 1987-2009 by the citadel.org team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #if TIME_WITH_SYS_TIME
22 # include <sys/time.h>
26 # include <sys/time.h>
33 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <arpa/inet.h>
42 #include <netinet/in.h>
46 #ifdef THREADED_CLIENT
49 #include <libcitadel.h>
51 #include "citadel_ipc.h"
52 #include "citadel_decls.h"
53 #include "citadel_dirs.h"
54 #ifdef THREADED_CLIENT
55 pthread_mutex_t rwlock;
59 static SSL_CTX *ssl_ctx;
62 #ifdef THREADED_CLIENT
63 pthread_mutex_t **Critters; /* Things that need locking */
64 #endif /* THREADED_CLIENT */
66 #endif /* HAVE_OPENSSL */
69 #define INADDR_NONE 0xffffffff
72 static void (*status_hook)(char *s) = NULL;
74 void setCryptoStatusHook(void (*hook)(char *s)) {
78 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
79 ipc->network_status_cb = hook;
83 char instant_msgs = 0;
86 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
87 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
89 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
90 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
91 static void endtls(SSL *ssl);
92 #ifdef THREADED_CLIENT
93 static unsigned long id_callback(void);
94 #endif /* THREADED_CLIENT */
95 #endif /* HAVE_OPENSSL */
96 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
97 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
101 const char *svn_revision(void);
104 * Does nothing. The server should always return 200.
106 int CtdlIPCNoop(CtdlIPC *ipc)
110 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
115 * Does nothing interesting. The server should always return 200
116 * along with your string.
118 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
124 if (!cret) return -2;
126 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
129 sprintf(aaa, "ECHO %s", arg);
130 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
137 * Asks the server to close the connecction.
138 * Should always return 200.
140 int CtdlIPCQuit(CtdlIPC *ipc)
142 register int ret = 221; /* Default to successful quit */
146 if (ipc->sock > -1) {
147 CtdlIPC_putline(ipc, "QUIT");
148 CtdlIPC_getline(ipc, aaa);
153 SSL_shutdown(ipc->ssl);
157 shutdown(ipc->sock, 2); /* Close connection; we're dead */
165 * Asks the server to log out. Should always return 200, even if no user
166 * was logged in. The user will not be logged in after this!
168 int CtdlIPCLogout(CtdlIPC *ipc)
174 CtdlIPC_putline(ipc, "LOUT");
175 CtdlIPC_getline(ipc, aaa);
183 * First stage of authentication - pass the username. Returns 300 if the
184 * username is able to log in, with the username correctly spelled in cret.
185 * Returns various 500 error codes if the user doesn't exist, etc.
187 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
192 if (!username) return -2;
193 if (!cret) return -2;
195 aaa = (char *)malloc((size_t)(strlen(username) + 6));
198 sprintf(aaa, "USER %s", username);
199 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
206 * Second stage of authentication - provide password. The server returns
207 * 200 and several arguments in cret relating to the user's account.
209 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
214 if (!passwd) return -2;
215 if (!cret) return -2;
217 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
220 sprintf(aaa, "PASS %s", passwd);
221 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
228 * Second stage of authentication - provide password. The server returns
229 * 200 and several arguments in cret relating to the user's account.
231 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
236 if (!response) return -2;
237 if (!cret) return -2;
239 aaa = (char *)malloc((size_t)(strlen(response) + 6));
242 sprintf(aaa, "PAS2 %s", response);
243 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
250 * Create a new user. This returns 200 plus the same arguments as TryPassword
251 * if selfservice is nonzero, unless there was a problem creating the account.
252 * If selfservice is zero, creates a new user but does not log out the existing
253 * user - intended for use by system administrators to create accounts on
254 * behalf of other users.
256 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
261 if (!username) return -2;
262 if (!cret) return -2;
264 aaa = (char *)malloc((size_t)(strlen(username) + 6));
267 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
268 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
275 * Changes the user's password. Returns 200 if changed, errors otherwise.
277 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
282 if (!passwd) return -2;
283 if (!cret) return -2;
285 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
288 sprintf(aaa, "SETP %s", passwd);
289 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
296 /* Caller must free the march list */
297 /* Room types are defined in enum RoomList; keep these in sync! */
298 /* floor is -1 for all, or floornum */
299 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
302 struct march *march = NULL;
303 static char *proto[] =
304 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
309 if (!listing) return -2;
310 if (*listing) return -2; /* Free the listing first */
311 if (!cret) return -2;
312 /* if (which < 0 || which > 4) return -2; */
313 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
315 sprintf(aaa, "%s %d", proto[which], floor);
316 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
317 if (ret / 100 == 1) {
320 while (bbb && strlen(bbb)) {
323 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
325 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
326 mptr = (struct march *) malloc(sizeof (struct march));
329 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
330 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
331 mptr->march_floor = (char) extract_int(aaa, 2);
332 mptr->march_order = (char) extract_int(aaa, 3);
333 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
334 mptr->march_access = (char) extract_int(aaa, 5);
341 while (mptr2->next != NULL)
355 /* Caller must free the struct ctdluser; caller may pass an existing one */
356 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
360 if (!cret) return -2;
361 if (!uret) return -2;
362 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
363 if (!*uret) return -1;
365 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
366 if (ret / 100 == 2) {
367 uret[0]->USscreenwidth = extract_int(cret, 0);
368 uret[0]->USscreenheight = extract_int(cret, 1);
369 uret[0]->flags = extract_int(cret, 2);
376 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
380 if (!uret) return -2;
381 if (!cret) return -2;
383 sprintf(aaa, "SETU %d|%d|%d",
384 uret->USscreenwidth, uret->USscreenheight,
386 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
391 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
396 if (!oldname) return -2;
397 if (!newname) return -2;
398 if (!cret) return -2;
400 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
401 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
407 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
408 struct ctdlipcroom **rret, char *cret)
413 if (!cret) return -2;
414 if (!rret) return -2;
415 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
416 if (!*rret) return -1;
419 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
424 sprintf(aaa, "GOTO %s|%s", room, passwd);
426 aaa = (char *)malloc(strlen(room) + 6);
431 sprintf(aaa, "GOTO %s", room);
433 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
434 if (ret / 100 == 2) {
435 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
436 rret[0]->RRunread = extract_long(cret, 1);
437 rret[0]->RRtotal = extract_long(cret, 2);
438 rret[0]->RRinfoupdated = extract_int(cret, 3);
439 rret[0]->RRflags = extract_int(cret, 4);
440 rret[0]->RRhighest = extract_long(cret, 5);
441 rret[0]->RRlastread = extract_long(cret, 6);
442 rret[0]->RRismailbox = extract_int(cret, 7);
443 rret[0]->RRaide = extract_int(cret, 8);
444 rret[0]->RRnewmail = extract_long(cret, 9);
445 rret[0]->RRfloor = extract_int(cret, 10);
446 rret[0]->RRcurrentview = extract_int(cret, 11);
447 rret[0]->RRdefaultview = extract_int(cret, 12);
448 /* position 13 is a trash folder flag ... irrelevant in this client */
449 rret[0]->RRflags2 = extract_int(cret, 14);
460 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
461 /* whicharg is number of messages, applies to last, first, gt, lt */
462 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
463 const char *mtemplate, unsigned long **mret, char *cret)
466 register unsigned long count = 0;
467 static char *proto[] =
468 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
473 if (!cret) return -2;
474 if (!mret) return -2;
475 if (*mret) return -2;
476 if (which < 0 || which > 6) return -2;
479 sprintf(aaa, "MSGS %s||%d", proto[which],
480 (mtemplate) ? 1 : 0);
482 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
483 (mtemplate) ? 1 : 0);
484 if (mtemplate) count = strlen(mtemplate);
485 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
489 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
492 while (bbb && strlen(bbb)) {
493 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
494 remove_token(bbb, 0, '\n');
495 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
496 sizeof (unsigned long)));
498 (*mret)[count++] = atol(aaa);
510 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
511 struct ctdlipcmessage **mret, char *cret)
517 int multipart_hunting = 0;
518 char multipart_prefix[128];
521 if (!cret) return -1;
522 if (!mret) return -1;
523 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
524 if (!*mret) return -1;
525 if (!msgnum) return -1;
527 strcpy(encoding, "");
528 strcpy(mret[0]->content_type, "");
529 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
530 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
531 if (ret / 100 == 1) {
533 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
534 while (strlen(bbb) > 4 && bbb[4] == '=') {
535 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
536 remove_token(bbb, 0, '\n');
538 if (!strncasecmp(aaa, "nhdr=yes", 8))
540 else if (!strncasecmp(aaa, "from=", 5))
541 safestrncpy(mret[0]->author, &aaa[5], SIZ);
542 else if (!strncasecmp(aaa, "type=", 5))
543 mret[0]->type = atoi(&aaa[5]);
544 else if (!strncasecmp(aaa, "msgn=", 5))
545 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
546 else if (!strncasecmp(aaa, "subj=", 5))
547 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
548 else if (!strncasecmp(aaa, "rfca=", 5))
549 safestrncpy(mret[0]->email, &aaa[5], SIZ);
550 else if (!strncasecmp(aaa, "hnod=", 5))
551 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
552 else if (!strncasecmp(aaa, "room=", 5))
553 safestrncpy(mret[0]->room, &aaa[5], SIZ);
554 else if (!strncasecmp(aaa, "node=", 5))
555 safestrncpy(mret[0]->node, &aaa[5], SIZ);
556 else if (!strncasecmp(aaa, "rcpt=", 5))
557 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
558 else if (!strncasecmp(aaa, "wefw=", 5))
559 safestrncpy(mret[0]->references, &aaa[5], SIZ);
560 else if (!strncasecmp(aaa, "time=", 5))
561 mret[0]->time = atol(&aaa[5]);
563 /* Multipart/alternative prefix & suffix strings help
564 * us to determine which part we want to download.
566 else if (!strncasecmp(aaa, "pref=", 5)) {
567 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
568 if (!strcasecmp(multipart_prefix,
569 "multipart/alternative")) {
573 else if (!strncasecmp(aaa, "suff=", 5)) {
574 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
575 if (!strcasecmp(multipart_prefix,
576 "multipart/alternative")) {
581 else if (!strncasecmp(aaa, "part=", 5)) {
582 struct parts *ptr, *chain;
584 ptr = (struct parts *)calloc(1, sizeof (struct parts));
587 /* Fill the buffers for the caller */
588 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
589 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
590 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
591 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
592 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
593 ptr->length = extract_long(&aaa[5], 5);
594 if (!mret[0]->attachments)
595 mret[0]->attachments = ptr;
597 chain = mret[0]->attachments;
603 /* Now handle multipart/alternative */
604 if (multipart_hunting > 0) {
605 if ( (!strcasecmp(ptr->mimetype,
607 || (!strcasecmp(ptr->mimetype,
609 strcpy(mret[0]->mime_chosen,
617 /* Eliminate "text\n" */
618 remove_token(bbb, 0, '\n');
620 /* If doing a MIME thing, pull out the extra headers */
623 if (!strncasecmp(bbb, "Content-type:", 13)) {
624 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
625 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
626 striplt(mret[0]->content_type);
628 /* strip out ";charset=" portion. FIXME do something with
629 * the charset (like... convert it) instead of just throwing
632 if (strstr(mret[0]->content_type, ";") != NULL) {
633 strcpy(strstr(mret[0]->content_type, ";"), "");
637 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
638 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
639 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
640 striplt(mret[0]->mime_chosen);
642 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
643 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
644 strcpy(encoding, &encoding[26]);
647 remove_token(bbb, 0, '\n');
648 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
649 remove_token(bbb, 0, '\n');
656 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
658 int bytes_decoded = 0;
659 ccc = malloc(strlen(bbb) + 32768);
660 if (!strcasecmp(encoding, "base64")) {
661 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
663 else if (!strcasecmp(encoding, "quoted-printable")) {
664 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
666 ccc[bytes_decoded] = 0;
671 /* FIXME: Strip trailing whitespace */
672 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
675 bbb = (char *)realloc(bbb, 1);
685 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
690 if (!cret) return -2;
691 if (!listing) return -2;
692 if (*listing) return -2;
694 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
700 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
704 char *listing = NULL;
707 if (!cret) return -2;
709 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
710 if (ret / 100 == 1) {
713 while (*listing && strlen(listing)) {
714 extract_token(buf, listing, 0, '\n', sizeof buf);
715 remove_token(listing, 0, '\n');
717 case 0: ipc->ServInfo.pid = atoi(buf);
719 case 1: strcpy(ipc->ServInfo.nodename,buf);
721 case 2: strcpy(ipc->ServInfo.humannode,buf);
723 case 3: strcpy(ipc->ServInfo.fqdn,buf);
725 case 4: strcpy(ipc->ServInfo.software,buf);
727 case 5: ipc->ServInfo.rev_level = atoi(buf);
729 case 6: strcpy(ipc->ServInfo.site_location,buf);
731 case 7: strcpy(ipc->ServInfo.sysadm,buf);
733 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
735 case 10: ipc->ServInfo.ok_floors = atoi(buf);
737 case 11: ipc->ServInfo.paging_level = atoi(buf);
739 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
741 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
743 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
745 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
747 case 17: ipc->ServInfo.load_avg = atof(buf);
749 case 18: ipc->ServInfo.worker_avg = atof(buf);
751 case 19: ipc->ServInfo.thread_count = atoi(buf);
753 case 20: ipc->ServInfo.has_sieve = atoi(buf);
755 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
757 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
759 case 24: ipc->ServInfo.guest_logins = atoi(buf);
765 if (listing) free(listing);
771 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
776 if (!cret) return -2;
777 if (!listing) return -2;
778 if (*listing) return -2;
780 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
786 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
788 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
793 if (!cret) return -2;
796 sprintf(aaa, "SLRP %ld", msgnum);
799 sprintf(aaa, "SLRP HIGHEST");
801 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
807 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
812 if (!cret) return -2;
813 if (!username) return -2;
815 aaa = (char *)malloc(strlen(username) + 6);
818 sprintf(aaa, "INVT %s", username);
819 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
826 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
831 if (!cret) return -1;
832 if (!username) return -1;
834 aaa = (char *)malloc(strlen(username) + 6);
836 sprintf(aaa, "KICK %s", username);
837 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
844 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
848 if (!cret) return -2;
849 if (!qret) return -2;
850 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
851 if (!*qret) return -1;
853 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
854 if (ret / 100 == 2) {
855 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
856 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
857 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
858 qret[0]->QRflags = extract_int(cret, 3);
859 qret[0]->QRfloor = extract_int(cret, 4);
860 qret[0]->QRorder = extract_int(cret, 5);
861 qret[0]->QRdefaultview = extract_int(cret, 6);
862 qret[0]->QRflags2 = extract_int(cret, 7);
869 /* set forget to kick all users out of room */
870 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
875 if (!cret) return -2;
876 if (!qret) return -2;
878 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
879 strlen(qret->QRdirname) + 64);
882 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
883 qret->QRname, qret->QRpasswd, qret->QRdirname,
884 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
885 qret->QRdefaultview, qret->QRflags2);
886 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
893 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
895 if (!cret) return -1;
897 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
902 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
907 if (!cret) return -2;
908 if (!username) return -2;
910 aaa = (char *)malloc(strlen(username) + 6);
913 sprintf(aaa, "SETA %s", username);
914 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
921 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
927 if (!cret) return -2;
930 if (mr->references) {
931 for (ptr=mr->references; *ptr != 0; ++ptr) {
932 if (*ptr == '|') *ptr = '!';
936 snprintf(cmd, sizeof cmd,
937 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
938 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
939 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
941 if ((flag == 0) && (subject_required != NULL)) {
942 /* Is the server strongly recommending that the user enter a message subject? */
943 if ((cret[3] != '\0') && (cret[4] != '\0')) {
944 *subject_required = extract_int(&cret[4], 1);
954 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
958 if (!cret) return -2;
959 if (!iret) return -2;
960 if (*iret) return -2;
962 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
967 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
971 if (!cret) return -2;
972 if (!msgnum) return -2;
974 sprintf(aaa, "DELE %ld", msgnum);
975 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
980 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
985 if (!cret) return -2;
986 if (!destroom) return -2;
987 if (!msgnum) return -2;
989 aaa = (char *)malloc(strlen(destroom) + 28);
992 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
993 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1000 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
1004 if (!cret) return -2;
1006 sprintf(aaa, "KILL %d", for_real);
1007 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1012 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
1013 const char *password, int floor, char *cret)
1018 if (!cret) return -2;
1019 if (!roomname) return -2;
1022 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
1023 if (!aaa) return -1;
1024 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
1027 aaa = (char *)malloc(strlen(roomname) + 40);
1028 if (!aaa) return -1;
1029 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1032 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1039 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1041 if (!cret) return -2;
1043 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1048 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1054 if (!cret) return -2;
1055 if (!mret) return -2;
1056 if (*mret) return -2;
1057 if (!message) return -2;
1059 aaa = (char *)malloc(strlen(message) + 6);
1060 if (!aaa) return -1;
1062 sprintf(aaa, "MESG %s", message);
1063 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1070 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1072 if (!cret) return -2;
1074 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1079 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1085 if (!cret) return -2;
1086 if (!rret) return -2;
1087 if (*rret) return -2;
1090 aaa = (char *)malloc(strlen(username) + 6);
1092 aaa = (char *)malloc(12);
1093 if (!aaa) return -1;
1096 sprintf(aaa, "GREG %s", username);
1098 sprintf(aaa, "GREG _SELF_");
1099 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1106 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1111 if (!cret) return -2;
1112 if (!username) return -2;
1113 if (axlevel < AxDeleted || axlevel > AxAideU) return -2;
1115 aaa = (char *)malloc(strlen(username) + 17);
1116 if (!aaa) return -1;
1118 sprintf(aaa, "VALI %s|%d", username, axlevel);
1119 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1126 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1130 if (!cret) return -1;
1131 if (!info) return -1;
1133 sprintf(aaa, "EINF %d", for_real);
1134 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1139 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1145 if (!cret) return -1;
1146 if (!listing) return -1;
1147 if (*listing) return -1;
1148 if (!searchstring) return -1;
1150 cmd = malloc(strlen(searchstring) + 10);
1151 sprintf(cmd, "LIST %s", searchstring);
1153 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1160 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1162 if (!cret) return -1;
1163 if (!info) return -1;
1165 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1171 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1175 if (!cret) return -1;
1176 if (!chek) return -1;
1178 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1179 if (ret / 100 == 2) {
1180 chek->newmail = extract_long(cret, 0);
1181 chek->needregis = extract_int(cret, 1);
1182 chek->needvalid = extract_int(cret, 2);
1189 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1194 if (!cret) return -2;
1195 if (!filename) return -2;
1197 aaa = (char *)malloc(strlen(filename) + 6);
1198 if (!aaa) return -1;
1200 sprintf(aaa, "DELF %s", filename);
1201 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1208 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1213 if (!cret) return -2;
1214 if (!filename) return -2;
1215 if (!destroom) return -2;
1217 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1218 if (!aaa) return -1;
1220 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1221 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1228 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1233 if (!cret) return -1;
1234 if (!listing) return -1;
1235 if (*listing) return -1;
1237 *stamp = CtdlIPCServerTime(ipc, cret);
1239 *stamp = time(NULL);
1240 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1246 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1248 void (*progress_gauge_callback)
1249 (CtdlIPC*, unsigned long, unsigned long),
1258 if (!cret) return -2;
1259 if (!filename) return -2;
1260 if (!buf) return -2;
1261 if (*buf) return -2;
1262 if (ipc->downloading) return -2;
1264 aaa = (char *)malloc(strlen(filename) + 6);
1265 if (!aaa) return -1;
1267 sprintf(aaa, "OPEN %s", filename);
1268 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1270 if (ret / 100 == 2) {
1271 ipc->downloading = 1;
1272 bytes = extract_long(cret, 0);
1273 last_mod = extract_int(cret, 1);
1274 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1276 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1277 progress_gauge_callback, cret);
1279 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1280 progress_gauge_callback, cret);
1283 ret = CtdlIPCEndDownload(ipc, cret);
1285 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1286 filename, mimetype);
1293 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1295 void (*progress_gauge_callback)
1296 (CtdlIPC*, unsigned long, unsigned long),
1306 if (!cret) return -2;
1307 if (!buf) return -2;
1308 if (*buf) return -2;
1309 if (!part) return -2;
1310 if (!msgnum) return -2;
1311 if (ipc->downloading) return -2;
1313 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1314 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1315 if (ret / 100 == 2) {
1316 ipc->downloading = 1;
1317 bytes = extract_long(cret, 0);
1318 last_mod = extract_int(cret, 1);
1319 extract_token(filename, cret, 2, '|', sizeof filename);
1320 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1321 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1322 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1323 ret = CtdlIPCEndDownload(ipc, cret);
1325 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1326 filename, mimetype);
1333 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1334 void (*progress_gauge_callback)
1335 (CtdlIPC*, unsigned long, unsigned long),
1344 if (!cret) return -1;
1345 if (!buf) return -1;
1346 if (*buf) return -1;
1347 if (!filename) return -1;
1348 if (ipc->downloading) return -1;
1350 aaa = (char *)malloc(strlen(filename) + 6);
1351 if (!aaa) return -1;
1353 sprintf(aaa, "OIMG %s", filename);
1354 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1356 if (ret / 100 == 2) {
1357 ipc->downloading = 1;
1358 bytes = extract_long(cret, 0);
1359 last_mod = extract_int(cret, 1);
1360 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1361 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1362 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1363 ret = CtdlIPCEndDownload(ipc, cret);
1365 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1366 filename, mimetype);
1373 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1375 void (*progress_gauge_callback)
1376 (CtdlIPC*, unsigned long, unsigned long),
1382 char MimeTestBuf[64];
1383 const char *MimeType;
1386 if (!cret) return -1;
1387 if (!save_as) return -1;
1388 if (!comment) return -1;
1389 if (!path) return -1;
1390 if (!*path) return -1;
1391 if (ipc->uploading) return -1;
1393 uploadFP = fopen(path, "r");
1394 if (!uploadFP) return -2;
1396 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1401 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1402 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1403 if (!aaa) return -1;
1405 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1406 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1408 if (ret / 100 == 2) {
1410 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1411 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1419 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1420 const char *save_as,
1421 void (*progress_gauge_callback)
1422 (CtdlIPC*, unsigned long, unsigned long),
1428 char MimeTestBuf[64];
1429 const char *MimeType;
1432 if (!cret) return -1;
1433 if (!save_as) return -1;
1434 if (!path && for_real) return -1;
1435 if (!*path && for_real) return -1;
1436 if (ipc->uploading) return -1;
1438 aaa = (char *)malloc(strlen(save_as) + 17);
1439 if (!aaa) return -1;
1441 uploadFP = fopen(path, "r");
1442 if (!uploadFP) return -2;
1444 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1448 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1450 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1451 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1453 if (ret / 100 == 2 && for_real) {
1455 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1456 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1464 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1469 if (!cret) return -2;
1470 if (!username) return -2;
1472 aaa = (char *)malloc(strlen(username) + 6);
1473 if (!aaa) return -1;
1475 sprintf(aaa, "QUSR %s", username);
1476 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1483 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1487 if (!cret) return -2;
1488 if (!listing) return -2;
1489 if (*listing) return -2;
1491 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1496 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1501 if (!cret) return -2;
1502 if (!name) return -2;
1504 sprintf(aaa, "CFLR %s|%d", name, for_real);
1505 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1511 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1515 if (!cret) return -1;
1516 if (floornum < 0) return -1;
1518 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1519 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1524 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1529 if (!cret) return -2;
1530 if (!floorname) return -2;
1531 if (floornum < 0) return -2;
1533 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1534 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1542 * You only need to fill out hostname, the defaults will be used if any of the
1543 * other fields are not set properly.
1545 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1546 int revision, const char *software_name, const char *hostname,
1552 if (developerid < 0 || clientid < 0 || revision < 0 ||
1556 revision = REV_LEVEL - 600;
1557 software_name = "Citadel (libcitadel)";
1559 if (!hostname) return -2;
1561 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1562 if (!aaa) return -1;
1564 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1565 revision, software_name, hostname);
1566 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1573 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1579 if (!cret) return -2;
1580 if (!username) return -2;
1582 aaa = (char *)malloc(strlen(username) + 8);
1583 if (!aaa) return -1;
1586 sprintf(aaa, "SEXP %s|-", username);
1587 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1590 sprintf(aaa, "SEXP %s||", username);
1591 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1599 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1603 if (!cret) return -2;
1604 if (!listing) return -2;
1605 if (*listing) return -2;
1607 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1612 /* mode is 0 = enable, 1 = disable, 2 = status */
1613 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1617 if (!cret) return -2;
1619 sprintf(aaa, "DEXP %d", mode);
1620 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1625 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1627 if (!cret) return -2;
1628 if (!bio) return -2;
1630 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1636 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1642 if (!cret) return -2;
1643 if (!username) return -2;
1644 if (!listing) return -2;
1645 if (*listing) return -2;
1647 aaa = (char *)malloc(strlen(username) + 6);
1648 if (!aaa) return -1;
1650 sprintf(aaa, "RBIO %s", username);
1651 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1658 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1662 if (!cret) return -2;
1663 if (!listing) return -2;
1664 if (*listing) return -2;
1666 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1671 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1675 if (!cret) return -1;
1677 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1678 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1683 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1687 if (!cret) return -1;
1689 sprintf(aaa, "TERM %d", sid);
1690 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1695 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1697 if (!cret) return -1;
1699 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1704 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1708 if (!cret) return -1;
1710 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1711 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1716 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1722 if (!cret) return -2;
1723 if (!text) return -2;
1724 if (!filename) return -2;
1726 aaa = (char *)malloc(strlen(filename) + 6);
1727 if (!aaa) return -1;
1729 sprintf(aaa, "EMSG %s", filename);
1730 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1737 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1742 if (!cret) return -2;
1743 if (!hostname) return -2;
1745 aaa = (char *)malloc(strlen(hostname) + 6);
1746 if (!aaa) return -1;
1748 sprintf(aaa, "HCHG %s", hostname);
1749 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1756 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1761 if (!cret) return -2;
1762 if (!roomname) return -2;
1764 aaa = (char *)malloc(strlen(roomname) + 6);
1765 if (!aaa) return -1;
1767 sprintf(aaa, "RCHG %s", roomname);
1768 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1775 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1780 if (!cret) return -2;
1781 if (!username) return -2;
1783 aaa = (char *)malloc(strlen(username) + 6);
1784 if (!aaa) return -1;
1786 sprintf(aaa, "UCHG %s", username);
1787 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1794 /* This function returns the actual server time reported, or 0 if error */
1795 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1797 register time_t tret;
1800 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1801 if (ret / 100 == 2) {
1802 tret = extract_long(cret, 0);
1811 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1812 struct ctdluser **uret, char *cret)
1817 if (!cret) return -2;
1818 if (!uret) return -2;
1819 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1820 if (!*uret) return -1;
1822 sprintf(aaa, "AGUP %s", who);
1823 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1825 if (ret / 100 == 2) {
1826 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1827 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1828 uret[0]->flags = extract_int(cret, 2);
1829 uret[0]->timescalled = extract_long(cret, 3);
1830 uret[0]->posted = extract_long(cret, 4);
1831 uret[0]->axlevel = extract_int(cret, 5);
1832 uret[0]->usernum = extract_long(cret, 6);
1833 uret[0]->lastcall = extract_long(cret, 7);
1834 uret[0]->USuserpurge = extract_int(cret, 8);
1841 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1846 if (!cret) return -2;
1847 if (!uret) return -2;
1849 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1850 if (!aaa) return -1;
1852 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1853 uret->fullname, uret->password, uret->flags,
1854 uret->timescalled, uret->posted, uret->axlevel,
1855 uret->usernum, uret->lastcall, uret->USuserpurge);
1856 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1863 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1864 /* caller must free the struct ExpirePolicy */
1865 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
1866 struct ExpirePolicy **policy, char *cret)
1868 static char *proto[] = {
1872 strof(mailboxespolicy)
1877 if (!cret) return -2;
1878 if (!policy) return -2;
1879 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1880 if (!*policy) return -1;
1881 if (which < 0 || which > 3) return -2;
1883 sprintf(cmd, "GPEX %s", proto[which]);
1884 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1885 if (ret / 100 == 2) {
1886 policy[0]->expire_mode = extract_int(cret, 0);
1887 policy[0]->expire_value = extract_int(cret, 1);
1894 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1895 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1896 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1897 struct ExpirePolicy *policy, char *cret)
1900 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1902 if (!cret) return -2;
1903 if (which < 0 || which > 3) return -2;
1904 if (!policy) return -2;
1905 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1906 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1908 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1909 policy->expire_mode, policy->expire_value);
1910 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1915 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1919 if (!cret) return -2;
1920 if (!listing) return -2;
1921 if (*listing) return -2;
1923 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1924 listing, &bytes, cret);
1929 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1931 if (!cret) return -2;
1932 if (!listing) return -2;
1934 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1940 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1941 char **listing, char *cret)
1947 if (!cret) return -2;
1948 if (!mimetype) return -2;
1949 if (!listing) return -2;
1950 if (*listing) return -2;
1952 aaa = malloc(strlen(mimetype) + 13);
1953 if (!aaa) return -1;
1954 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1955 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1956 listing, &bytes, cret);
1963 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1964 const char *listing, char *cret)
1969 if (!cret) return -2;
1970 if (!mimetype) return -2;
1971 if (!listing) return -2;
1973 aaa = malloc(strlen(mimetype) + 13);
1974 if (!aaa) return -1;
1975 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1976 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1984 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1988 if (!cret) return -2;
1989 if (!listing) return -2;
1990 if (*listing) return -2;
1992 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1993 listing, &bytes, cret);
1998 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
2000 if (!cret) return -2;
2001 if (!listing) return -2;
2003 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
2009 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2013 if (!cret) return -2;
2014 if (session < 0) return -2;
2016 sprintf(aaa, "REQT %d", session);
2017 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2022 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2026 if (!cret) return -2;
2027 if (msgnum < 0) return -2;
2029 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2030 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2035 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2044 /* New SSL object */
2045 temp_ssl = SSL_new(ssl_ctx);
2047 error_printf("SSL_new failed: %s\n",
2048 ERR_reason_error_string(ERR_get_error()));
2051 /* Pointless flag waving */
2052 #if SSLEAY_VERSION_NUMBER >= 0x0922
2053 SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
2056 if (!access(EGD_POOL, F_OK))
2059 if (!RAND_status()) {
2060 error_printf("PRNG not properly seeded\n");
2064 /* Associate network connection with SSL object */
2065 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2066 error_printf("SSL_set_fd failed: %s\n",
2067 ERR_reason_error_string(ERR_get_error()));
2071 if (status_hook != NULL)
2072 status_hook("Requesting encryption...\r");
2074 /* Ready to start SSL/TLS */
2076 CtdlIPC_putline(ipc, "STLS");
2077 CtdlIPC_getline(ipc, buf);
2078 if (buf[0] != '2') {
2079 error_printf("Server can't start TLS: %s\n", buf);
2083 r = CtdlIPCGenericCommand(ipc,
2084 "STLS", NULL, 0, NULL, NULL, cret);
2086 error_printf("Server can't start TLS: %s\n", buf);
2091 /* Do SSL/TLS handshake */
2092 if ((a = SSL_connect(temp_ssl)) < 1) {
2093 error_printf("SSL_connect failed: %s\n",
2094 ERR_reason_error_string(ERR_get_error()));
2098 ipc->ssl = temp_ssl;
2100 if (BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE))
2104 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2105 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2106 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2107 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2113 #endif /* HAVE_OPENSSL */
2118 static void endtls(SSL *ssl)
2129 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2134 if (!address) return -2;
2135 if (!cret) return -2;
2137 aaa = (char *)malloc(strlen(address) + 6);
2138 if (!aaa) return -1;
2140 sprintf(aaa, "QDIR %s", address);
2141 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2148 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2152 if (!cret) return -2;
2153 sprintf(aaa, "IPGM %d", secret);
2154 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2159 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2163 if (!cret) return -2;
2164 if (!mret) return -2;
2165 if (*mret) return -2;
2167 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2188 /* ************************************************************************** */
2189 /* Stuff below this line is not for public consumption */
2190 /* ************************************************************************** */
2193 /* Read a listing from the server up to 000. Append to dest if it exists */
2194 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2203 length = strlen(ret);
2208 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2209 linelength = strlen(aaa);
2210 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2212 strcpy(&ret[length], aaa);
2213 length += linelength;
2214 strcpy(&ret[length++], "\n");
2222 /* Send a listing to the server; generate the ending 000. */
2223 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2227 text = (char *)malloc(strlen(listing) + 6);
2229 strcpy(text, listing);
2230 while (text[strlen(text) - 1] == '\n')
2231 text[strlen(text) - 1] = '\0';
2232 strcat(text, "\n000");
2233 CtdlIPC_putline(ipc, text);
2237 /* Malloc failed but we are committed to send */
2238 /* This may result in extra blanks at the bottom */
2239 CtdlIPC_putline(ipc, text);
2240 CtdlIPC_putline(ipc, "000");
2246 /* Partial read of file from server */
2247 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2249 register size_t len = 0;
2253 if (!cret) return 0;
2254 if (bytes < 1) return 0;
2257 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2258 CtdlIPC_putline(ipc, aaa);
2259 CtdlIPC_getline(ipc, aaa);
2261 strcpy(cret, &aaa[4]);
2263 len = extract_long(&aaa[4], 0);
2264 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2266 /* I know what I'm doing */
2267 serv_read(ipc, ((char *)(*buf) + offset), len);
2269 /* We have to read regardless */
2270 serv_read(ipc, aaa, len);
2274 CtdlIPC_unlock(ipc);
2280 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2284 if (!cret) return -2;
2285 if (!ipc->downloading) return -2;
2287 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2289 ipc->downloading = 0;
2295 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2299 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2300 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2307 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2308 void (*progress_gauge_callback)
2309 (CtdlIPC*, unsigned long, unsigned long),
2312 register size_t len;
2314 if (!cret) return -1;
2315 if (!buf) return -1;
2316 if (*buf) return -1;
2317 if (!ipc->downloading) return -1;
2320 if (progress_gauge_callback)
2321 progress_gauge_callback(ipc, len, bytes);
2322 while (len < bytes) {
2323 register size_t block;
2325 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2331 if (progress_gauge_callback)
2332 progress_gauge_callback(ipc, len, bytes);
2337 /* READ - pipelined */
2338 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2340 void (*progress_gauge_callback)
2341 (CtdlIPC*, unsigned long, unsigned long),
2344 register size_t len;
2345 register int calls; /* How many calls in the pipeline */
2346 register int i; /* iterator */
2349 if (!cret) return -1;
2350 if (!buf) return -1;
2351 if (*buf) return -1;
2352 if (!ipc->downloading) return -1;
2354 *buf = (void *)realloc(*buf, bytes - resume);
2355 if (!*buf) return -1;
2359 if (progress_gauge_callback)
2360 progress_gauge_callback(ipc, len, bytes);
2362 /* How many calls will be in the pipeline? */
2363 calls = (bytes - resume) / 4096;
2364 if ((bytes - resume) % 4096) calls++;
2366 /* Send all requests at once */
2367 for (i = 0; i < calls; i++) {
2368 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2369 CtdlIPC_putline(ipc, aaa);
2372 /* Receive all responses at once */
2373 for (i = 0; i < calls; i++) {
2374 CtdlIPC_getline(ipc, aaa);
2376 strcpy(cret, &aaa[4]);
2378 len = extract_long(&aaa[4], 0);
2379 /* I know what I'm doing */
2380 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2382 if (progress_gauge_callback)
2383 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2385 CtdlIPC_unlock(ipc);
2391 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2396 if (!cret) return -1;
2397 if (!ipc->uploading) return -1;
2399 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2400 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2407 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2408 void (*progress_gauge_callback)
2409 (CtdlIPC*, unsigned long, unsigned long),
2412 register int ret = -1;
2413 register size_t offset = 0;
2417 FILE *fd = uploadFP;
2420 if (!cret) return -1;
2422 fseek(fd, 0L, SEEK_END);
2426 if (progress_gauge_callback)
2427 progress_gauge_callback(ipc, 0, bytes);
2429 while (offset < bytes) {
2430 register size_t to_write;
2432 /* Read some data in */
2433 to_write = fread(buf, 1, 4096, fd);
2435 if (feof(fd) || ferror(fd)) break;
2437 sprintf(aaa, "WRIT %d", (int)to_write);
2438 CtdlIPC_putline(ipc, aaa);
2439 CtdlIPC_getline(ipc, aaa);
2440 strcpy(cret, &aaa[4]);
2442 if (aaa[0] == '7') {
2443 to_write = extract_long(&aaa[4], 0);
2445 serv_write(ipc, buf, to_write);
2447 if (progress_gauge_callback)
2448 progress_gauge_callback(ipc, offset, bytes);
2449 /* Detect short reads and back up if needed */
2450 /* offset will never be negative anyway */
2451 fseek(fd, (signed)offset, SEEK_SET);
2456 if (progress_gauge_callback)
2457 progress_gauge_callback(ipc, 1, 1);
2460 return (!ferr ? ret : -2);
2465 * Generic command method. This method should handle any server command
2466 * except for CHAT. It takes the following arguments:
2468 * ipc The server to speak with
2469 * command Preformatted command to send to server
2470 * to_send A text or binary file to send to server
2471 * (only sent if server requests it)
2472 * bytes_to_send The number of bytes in to_send (required if
2473 * sending binary, optional if sending listing)
2474 * to_receive Pointer to a NULL pointer, if the server
2475 * sends text or binary we will allocate memory
2476 * for the file and stuff it here
2477 * bytes_to_receive If a file is received, we will store its
2479 * proto_response The protocol response. Caller must provide
2480 * this buffer and ensure that it is at least
2481 * 128 bytes in length.
2483 * This function returns a number equal to the protocol response number,
2484 * -1 if an internal error occurred, -2 if caller provided bad values,
2485 * or 0 - the protocol response number if bad values were found during
2486 * the protocol exchange.
2487 * It stores the protocol response string (minus the number) in
2488 * protocol_response as described above. Some commands send additional
2489 * data in this string.
2491 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2492 const char *command, const char *to_send,
2493 size_t bytes_to_send, char **to_receive,
2494 size_t *bytes_to_receive, char *proto_response)
2500 if (!command) return -2;
2501 if (!proto_response) return -2;
2504 if (ipc->ssl) watch_ssl = 1;
2508 CtdlIPC_putline(ipc, command);
2510 CtdlIPC_getline(ipc, proto_response);
2511 if (proto_response[3] == '*')
2513 ret = atoi(proto_response);
2514 strcpy(proto_response, &proto_response[4]);
2515 switch (ret / 100) {
2516 default: /* Unknown, punt */
2518 case 3: /* MORE_DATA */
2520 /* Don't need to do anything */
2522 case 1: /* LISTING_FOLLOWS */
2523 if (to_receive && !*to_receive && bytes_to_receive) {
2524 *to_receive = CtdlIPCReadListing(ipc, NULL);
2525 } else { /* Drain */
2526 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2530 case 4: /* SEND_LISTING */
2532 CtdlIPCSendListing(ipc, to_send);
2534 /* No listing given, fake it */
2535 CtdlIPC_putline(ipc, "000");
2539 case 6: /* BINARY_FOLLOWS */
2540 if (to_receive && !*to_receive && bytes_to_receive) {
2542 extract_long(proto_response, 0);
2543 *to_receive = (char *)
2544 malloc((size_t)*bytes_to_receive);
2548 serv_read(ipc, *to_receive,
2555 drain = extract_long(proto_response, 0);
2556 while (drain > SIZ) {
2557 serv_read(ipc, buf, SIZ);
2560 serv_read(ipc, buf, drain);
2564 case 7: /* SEND_BINARY */
2565 if (to_send && bytes_to_send) {
2566 serv_write(ipc, to_send, bytes_to_send);
2567 } else if (bytes_to_send) {
2568 /* Fake it, send nulls */
2571 fake = bytes_to_send;
2572 memset(buf, '\0', SIZ);
2573 while (fake > SIZ) {
2574 serv_write(ipc, buf, SIZ);
2577 serv_write(ipc, buf, fake);
2579 } /* else who knows? DANGER WILL ROBINSON */
2581 case 8: /* START_CHAT_MODE */
2582 if (!strncasecmp(command, "CHAT", 4)) {
2583 /* Don't call chatmode with generic! */
2584 CtdlIPC_putline(ipc, "/quit");
2587 /* In this mode we send then receive listing */
2589 CtdlIPCSendListing(ipc, to_send);
2591 /* No listing given, fake it */
2592 CtdlIPC_putline(ipc, "000");
2595 if (to_receive && !*to_receive
2596 && bytes_to_receive) {
2597 *to_receive = CtdlIPCReadListing(ipc, NULL);
2598 } else { /* Drain */
2599 while (CtdlIPC_getline(ipc, buf),
2600 strcmp(buf, "000")) ;
2605 case 9: /* ASYNC_MSG */
2606 /* CtdlIPCDoAsync(ret, proto_response); */
2607 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2613 CtdlIPC_unlock(ipc);
2619 * Connect to a Citadel on a remote host using a TCP/IP socket
2621 static int tcp_connectsock(char *host, char *service)
2623 struct in6_addr serveraddr;
2624 struct addrinfo hints;
2625 struct addrinfo *res = NULL;
2626 struct addrinfo *ai = NULL;
2630 if ((host == NULL) || IsEmptyStr(host)) {
2631 service = DEFAULT_HOST ;
2633 if ((service == NULL) || IsEmptyStr(service)) {
2634 service = DEFAULT_PORT ;
2637 memset(&hints, 0x00, sizeof(hints));
2638 hints.ai_flags = AI_NUMERICSERV;
2639 hints.ai_family = AF_UNSPEC;
2640 hints.ai_socktype = SOCK_STREAM;
2643 * Handle numeric IPv4 and IPv6 addresses
2645 rc = inet_pton(AF_INET, host, &serveraddr);
2646 if (rc == 1) { /* dotted quad */
2647 hints.ai_family = AF_INET;
2648 hints.ai_flags |= AI_NUMERICHOST;
2651 rc = inet_pton(AF_INET6, host, &serveraddr);
2652 if (rc == 1) { /* IPv6 address */
2653 hints.ai_family = AF_INET6;
2654 hints.ai_flags |= AI_NUMERICHOST;
2658 /* Begin the connection process */
2660 rc = getaddrinfo(host, service, &hints, &res);
2666 * Try all available addresses until we connect to one or until we run out.
2668 for (ai = res; ai != NULL; ai = ai->ai_next) {
2669 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2670 if (sock < 0) return(-1);
2672 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2674 return(sock); /* Connected! */
2677 close(sock); /* Failed. Close the socket to avoid fd leak! */
2689 * Connect to a Citadel on the local host using a unix domain socket
2691 static int uds_connectsock(int *isLocal, char *sockpath)
2693 struct sockaddr_un addr;
2696 memset(&addr, 0, sizeof(addr));
2697 addr.sun_family = AF_UNIX;
2698 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2700 s = socket(AF_UNIX, SOCK_STREAM, 0);
2705 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2716 * input binary data from socket
2718 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2720 unsigned int len, rlen;
2722 #if defined(HAVE_OPENSSL)
2724 serv_read_ssl(ipc, buf, bytes);
2729 while (len < bytes) {
2730 rlen = read(ipc->sock, &buf[len], bytes - len);
2732 connection_died(ipc, 0);
2741 * send binary to server
2743 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2745 unsigned int bytes_written = 0;
2748 #if defined(HAVE_OPENSSL)
2750 serv_write_ssl(ipc, buf, nbytes);
2754 while (bytes_written < nbytes) {
2755 retval = write(ipc->sock, &buf[bytes_written],
2756 nbytes - bytes_written);
2758 connection_died(ipc, 0);
2761 bytes_written += retval;
2768 * input binary data from encrypted connection
2770 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2776 while (len < bytes) {
2777 if (SSL_want_read(ipc->ssl)) {
2778 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2779 error_printf("SSL_write in serv_read:\n");
2780 ERR_print_errors_fp(stderr);
2783 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2787 errval = SSL_get_error(ipc->ssl, rlen);
2788 if (errval == SSL_ERROR_WANT_READ ||
2789 errval == SSL_ERROR_WANT_WRITE) {
2794 Not sure why we'd want to handle these error codes any differently,
2795 but this definitely isn't the way to handle them. Someone must have
2796 naively assumed that we could fall back to unencrypted communications,
2797 but all it does is just recursively blow the stack.
2798 if (errval == SSL_ERROR_ZERO_RETURN ||
2799 errval == SSL_ERROR_SSL) {
2800 serv_read(ipc, &buf[len], bytes - len);
2804 error_printf("SSL_read in serv_read: %s\n",
2805 ERR_reason_error_string(ERR_peek_error()));
2806 connection_died(ipc, 1);
2815 * send binary to server encrypted
2817 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2819 unsigned int bytes_written = 0;
2823 while (bytes_written < nbytes) {
2824 if (SSL_want_write(ipc->ssl)) {
2825 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2826 error_printf("SSL_read in serv_write:\n");
2827 ERR_print_errors_fp(stderr);
2830 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2831 nbytes - bytes_written);
2835 errval = SSL_get_error(ipc->ssl, retval);
2836 if (errval == SSL_ERROR_WANT_READ ||
2837 errval == SSL_ERROR_WANT_WRITE) {
2841 if (errval == SSL_ERROR_ZERO_RETURN ||
2842 errval == SSL_ERROR_SSL) {
2843 serv_write(ipc, &buf[bytes_written],
2844 nbytes - bytes_written);
2847 error_printf("SSL_write in serv_write: %s\n",
2848 ERR_reason_error_string(ERR_peek_error()));
2849 connection_died(ipc, 1);
2852 bytes_written += retval;
2857 #ifdef THREADED_CLIENT
2858 static void ssl_lock(int mode, int n, const char *file, int line)
2860 if (mode & CRYPTO_LOCK)
2861 pthread_mutex_lock(Critters[n]);
2863 pthread_mutex_unlock(Critters[n]);
2865 #endif /* THREADED_CLIENT */
2868 static void CtdlIPC_init_OpenSSL(void)
2871 SSL_METHOD *ssl_method;
2874 /* already done init */
2883 SSL_load_error_strings();
2884 SSLeay_add_ssl_algorithms();
2886 /* Set up the SSL context in which we will oeprate */
2887 ssl_method = SSLv23_client_method();
2888 ssl_ctx = SSL_CTX_new(ssl_method);
2890 error_printf("SSL_CTX_new failed: %s\n",
2891 ERR_reason_error_string(ERR_get_error()));
2894 /* Any reasonable cipher we can get */
2895 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2896 error_printf("No ciphers available for encryption\n");
2899 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2901 /* Load DH parameters into the context */
2904 error_printf("Can't allocate a DH object: %s\n",
2905 ERR_reason_error_string(ERR_get_error()));
2908 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2909 error_printf("Can't assign DH_P: %s\n",
2910 ERR_reason_error_string(ERR_get_error()));
2914 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2915 error_printf("Can't assign DH_G: %s\n",
2916 ERR_reason_error_string(ERR_get_error()));
2921 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2924 #ifdef THREADED_CLIENT
2925 /* OpenSSL requires callbacks for threaded clients */
2926 CRYPTO_set_locking_callback(ssl_lock);
2927 CRYPTO_set_id_callback(id_callback);
2929 /* OpenSSL requires us to do semaphores for threaded clients */
2930 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2932 perror("malloc failed");
2935 for (a = 0; a < CRYPTO_num_locks(); a++) {
2936 Critters[a] = malloc(sizeof (pthread_mutex_t));
2938 perror("malloc failed");
2941 pthread_mutex_init(Critters[a], NULL);
2944 #endif /* THREADED_CLIENT */
2949 #ifdef THREADED_CLIENT
2950 static unsigned long id_callback(void) {
2951 return (unsigned long)pthread_self();
2953 #endif /* THREADED_CLIENT */
2954 #endif /* HAVE_OPENSSL */
2958 ReadNetworkChunk(CtdlIPC* ipc)
2975 FD_SET(ipc->sock, &read_fd);
2976 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2978 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2982 *(ipc->BufPtr) = '\0';
2983 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2984 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2986 ipc->BufPtr[n]='\0';
2994 if (!(errno == EINTR || errno == EAGAIN))
2995 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
3001 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3003 ipc->BufPtr[n]='\0';
3008 connection_died(ipc, 0);
3016 * input string from socket - implemented in terms of serv_read()
3020 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3023 char *aptr, *bptr, *aeptr, *beptr;
3025 // error_printf("---\n");
3028 #if defined(HAVE_OPENSSL)
3031 /* Read one character at a time. */
3033 serv_read(ipc, &buf[i], 1);
3034 if (buf[i] == '\n' || i == (SIZ-1))
3038 /* If we got a long line, discard characters until the newline. */
3040 while (buf[i] != '\n')
3041 serv_read(ipc, &buf[i], 1);
3043 /* Strip the trailing newline (and carriage return, if present) */
3044 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3045 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3050 if (ipc->Buf == NULL)
3053 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3055 ipc->BufPtr = ipc->Buf;
3059 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3060 if (ipc->BufUsed == 0)
3061 ReadNetworkChunk(ipc);
3063 //// if (ipc->BufUsed != 0) while (1)
3069 aeptr = ipc->Buf + ipc->BufSize;
3070 while ((aptr < aeptr) &&
3074 *(bptr++) = *(aptr++);
3075 if ((*aptr == '\n') && (aptr < aeptr))
3077 /* Terminate it right, remove the line breaks */
3078 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3080 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3083 // 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);
3084 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3087 /* is there more in the buffer we need to read later? */
3088 if (ipc->Buf + ipc->BufUsed > aptr)
3095 ipc->BufPtr = ipc->Buf;
3097 // error_printf("----bla6\n");
3100 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3101 else if ((ipc->BufPtr != ipc->Buf) &&
3102 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3104 size_t NewBufSize = ipc->BufSize * 2;
3105 int delta = (ipc->BufPtr - ipc->Buf);
3108 /* if the line would end after our buffer, we should use a bigger buffer. */
3109 NewBuf = (char *)malloc (NewBufSize + 10);
3110 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3112 ipc->Buf = ipc->BufPtr = NewBuf;
3113 ipc->BufUsed -= delta;
3114 ipc->BufSize = NewBufSize;
3116 if (ReadNetworkChunk(ipc) <0)
3118 // error_printf("----bla\n");
3122 /// error_printf("----bl45761%s\nipc->BufUsed");
3124 // error_printf("----bla1\n");
3127 #else /* CHUNKED_READ */
3129 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3133 /* Read one character at a time. */
3135 serv_read(ipc, &buf[i], 1);
3136 if (buf[i] == '\n' || i == (SIZ-1))
3140 /* If we got a long line, discard characters until the newline. */
3142 while (buf[i] != '\n')
3143 serv_read(ipc, &buf[i], 1);
3145 /* Strip the trailing newline (and carriage return, if present) */
3146 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3147 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3151 #endif /* CHUNKED_READ */
3154 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3156 CtdlIPC_getline(ipc, buf);
3160 * send line to server - implemented in terms of serv_write()
3162 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3168 cmd = malloc(len + 2);
3170 /* This requires no extra memory */
3171 serv_write(ipc, buf, len);
3172 serv_write(ipc, "\n", 1);
3174 /* This is network-optimized */
3175 strncpy(cmd, buf, len);
3176 strcpy(cmd + len, "\n");
3177 serv_write(ipc, cmd, len + 1);
3181 ipc->last_command_sent = time(NULL);
3184 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3186 CtdlIPC_putline(ipc, buf);
3193 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3201 ipc = ialloc(CtdlIPC);
3205 #if defined(HAVE_OPENSSL)
3207 CtdlIPC_init_OpenSSL();
3209 #if defined(HAVE_PTHREAD_H)
3210 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3212 ipc->sock = -1; /* Not connected */
3213 ipc->isLocal = 0; /* Not local, of course! */
3214 ipc->downloading = 0;
3216 ipc->last_command_sent = 0L;
3217 ipc->network_status_cb = NULL;
3222 strcpy(cithost, DEFAULT_HOST); /* default host */
3223 strcpy(citport, DEFAULT_PORT); /* default port */
3225 /* Allow caller to supply our values (Windows) */
3226 if (hostbuf && strlen(hostbuf) > 0)
3227 strcpy(cithost, hostbuf);
3228 if (portbuf && strlen(portbuf) > 0)
3229 strcpy(citport, portbuf);
3231 /* Read host/port from command line if present */
3232 for (a = 0; a < argc; ++a) {
3235 } else if (a == 1) {
3236 strcpy(cithost, argv[a]);
3237 } else if (a == 2) {
3238 strcpy(citport, argv[a]);
3240 error_printf("%s: usage: ",argv[0]);
3241 error_printf("%s [host] [port] ",argv[0]);
3248 if ((!strcmp(cithost, "localhost"))
3249 || (!strcmp(cithost, "127.0.0.1"))) {
3253 /* If we're using a unix domain socket we can do a bunch of stuff */
3254 if (!strcmp(cithost, UDS)) {
3255 if (!strcasecmp(citport, DEFAULT_PORT)) {
3256 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3259 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3261 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3262 if (ipc->sock == -1) {
3266 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3267 if (portbuf != NULL) strcpy(portbuf, sockpath);
3268 strcpy(ipc->ip_hostname, "");
3269 strcpy(ipc->ip_address, "");
3273 ipc->sock = tcp_connectsock(cithost, citport);
3274 if (ipc->sock == -1) {
3280 /* Learn the actual network identity of the host to which we are connected */
3282 struct sockaddr_in6 clientaddr;
3283 unsigned int addrlen = sizeof(clientaddr);
3285 ipc->ip_hostname[0] = 0;
3286 ipc->ip_address[0] = 0;
3288 getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3289 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3290 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3292 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3293 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3296 /* stuff other things elsewhere */
3298 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3299 if (portbuf != NULL) strcpy(portbuf, citport);
3305 * Disconnect and delete the IPC class (destructor)
3307 void CtdlIPC_delete(CtdlIPC* ipc)
3311 SSL_shutdown(ipc->ssl);
3316 if (ipc->sock > -1) {
3317 shutdown(ipc->sock, 2); /* Close it up */
3320 if (ipc->Buf != NULL)
3329 * Disconnect and delete the IPC class (destructor)
3330 * Also NULLs out the pointer
3332 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3334 CtdlIPC_delete(*pipc);
3340 * return the file descriptor of the server socket so we can select() on it.
3342 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3345 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3352 * return one character
3354 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3357 char CtdlIPC_get(CtdlIPC* ipc)
3362 serv_read(ipc, buf, 1);