2 * Copyright (c) 1987-2009 by the citadel.org team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #if TIME_WITH_SYS_TIME
21 # include <sys/time.h>
25 # include <sys/time.h>
32 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <arpa/inet.h>
41 #include <netinet/in.h>
45 #ifdef THREADED_CLIENT
48 #include <libcitadel.h>
50 #include "citadel_ipc.h"
51 #include "citadel_decls.h"
52 #include "citadel_dirs.h"
53 #ifdef THREADED_CLIENT
54 pthread_mutex_t rwlock;
58 static SSL_CTX *ssl_ctx;
61 #ifdef THREADED_CLIENT
62 pthread_mutex_t **Critters; /* Things that need locking */
63 #endif /* THREADED_CLIENT */
65 #endif /* HAVE_OPENSSL */
68 #define INADDR_NONE 0xffffffff
71 static void (*status_hook)(char *s) = NULL;
73 void setCryptoStatusHook(void (*hook)(char *s)) {
77 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
78 ipc->network_status_cb = hook;
82 char instant_msgs = 0;
85 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
86 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
88 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
89 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
90 static void endtls(SSL *ssl);
91 #ifdef THREADED_CLIENT
92 static unsigned long id_callback(void);
93 #endif /* THREADED_CLIENT */
94 #endif /* HAVE_OPENSSL */
95 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
96 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
100 const char *svn_revision(void);
103 * Does nothing. The server should always return 200.
105 int CtdlIPCNoop(CtdlIPC *ipc)
109 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
114 * Does nothing interesting. The server should always return 200
115 * along with your string.
117 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
123 if (!cret) return -2;
125 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
128 sprintf(aaa, "ECHO %s", arg);
129 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
136 * Asks the server to close the connecction.
137 * Should always return 200.
139 int CtdlIPCQuit(CtdlIPC *ipc)
141 register int ret = 221; /* Default to successful quit */
145 if (ipc->sock > -1) {
146 CtdlIPC_putline(ipc, "QUIT");
147 CtdlIPC_getline(ipc, aaa);
152 SSL_shutdown(ipc->ssl);
156 shutdown(ipc->sock, 2); /* Close connection; we're dead */
164 * Asks the server to log out. Should always return 200, even if no user
165 * was logged in. The user will not be logged in after this!
167 int CtdlIPCLogout(CtdlIPC *ipc)
173 CtdlIPC_putline(ipc, "LOUT");
174 CtdlIPC_getline(ipc, aaa);
182 * First stage of authentication - pass the username. Returns 300 if the
183 * username is able to log in, with the username correctly spelled in cret.
184 * Returns various 500 error codes if the user doesn't exist, etc.
186 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
191 if (!username) return -2;
192 if (!cret) return -2;
194 aaa = (char *)malloc((size_t)(strlen(username) + 6));
197 sprintf(aaa, "USER %s", username);
198 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
205 * Second stage of authentication - provide password. The server returns
206 * 200 and several arguments in cret relating to the user's account.
208 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
213 if (!passwd) return -2;
214 if (!cret) return -2;
216 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
219 sprintf(aaa, "PASS %s", passwd);
220 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
227 * Second stage of authentication - provide password. The server returns
228 * 200 and several arguments in cret relating to the user's account.
230 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
235 if (!response) return -2;
236 if (!cret) return -2;
238 aaa = (char *)malloc((size_t)(strlen(response) + 6));
241 sprintf(aaa, "PAS2 %s", response);
242 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
249 * Create a new user. This returns 200 plus the same arguments as TryPassword
250 * if selfservice is nonzero, unless there was a problem creating the account.
251 * If selfservice is zero, creates a new user but does not log out the existing
252 * user - intended for use by system administrators to create accounts on
253 * behalf of other users.
255 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
260 if (!username) return -2;
261 if (!cret) return -2;
263 aaa = (char *)malloc((size_t)(strlen(username) + 6));
266 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
267 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
274 * Changes the user's password. Returns 200 if changed, errors otherwise.
276 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
281 if (!passwd) return -2;
282 if (!cret) return -2;
284 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
287 sprintf(aaa, "SETP %s", passwd);
288 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
295 /* Caller must free the march list */
296 /* Room types are defined in enum RoomList; keep these in sync! */
297 /* floor is -1 for all, or floornum */
298 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
301 struct march *march = NULL;
302 static char *proto[] =
303 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
308 if (!listing) return -2;
309 if (*listing) return -2; /* Free the listing first */
310 if (!cret) return -2;
311 /* if (which < 0 || which > 4) return -2; */
312 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
314 sprintf(aaa, "%s %d", proto[which], floor);
315 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
316 if (ret / 100 == 1) {
319 while (bbb && strlen(bbb)) {
322 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
324 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
325 mptr = (struct march *) malloc(sizeof (struct march));
328 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
329 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
330 mptr->march_floor = (char) extract_int(aaa, 2);
331 mptr->march_order = (char) extract_int(aaa, 3);
332 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
333 mptr->march_access = (char) extract_int(aaa, 5);
340 while (mptr2->next != NULL)
354 /* Caller must free the struct ctdluser; caller may pass an existing one */
355 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
359 if (!cret) return -2;
360 if (!uret) return -2;
361 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
362 if (!*uret) return -1;
364 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
365 if (ret / 100 == 2) {
366 uret[0]->flags = extract_int(cret, 2);
373 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
377 if (!uret) return -2;
378 if (!cret) return -2;
384 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
389 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
394 if (!oldname) return -2;
395 if (!newname) return -2;
396 if (!cret) return -2;
398 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
399 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
405 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
406 struct ctdlipcroom **rret, char *cret)
411 if (!cret) return -2;
412 if (!rret) return -2;
413 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
414 if (!*rret) return -1;
417 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
422 sprintf(aaa, "GOTO %s|%s", room, passwd);
424 aaa = (char *)malloc(strlen(room) + 6);
429 sprintf(aaa, "GOTO %s", room);
431 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
432 if (ret / 100 == 2) {
433 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
434 rret[0]->RRunread = extract_long(cret, 1);
435 rret[0]->RRtotal = extract_long(cret, 2);
436 rret[0]->RRinfoupdated = extract_int(cret, 3);
437 rret[0]->RRflags = extract_int(cret, 4);
438 rret[0]->RRhighest = extract_long(cret, 5);
439 rret[0]->RRlastread = extract_long(cret, 6);
440 rret[0]->RRismailbox = extract_int(cret, 7);
441 rret[0]->RRaide = extract_int(cret, 8);
442 rret[0]->RRnewmail = extract_long(cret, 9);
443 rret[0]->RRfloor = extract_int(cret, 10);
444 rret[0]->RRcurrentview = extract_int(cret, 11);
445 rret[0]->RRdefaultview = extract_int(cret, 12);
446 /* position 13 is a trash folder flag ... irrelevant in this client */
447 rret[0]->RRflags2 = extract_int(cret, 14);
458 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
459 /* whicharg is number of messages, applies to last, first, gt, lt */
460 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
461 const char *mtemplate, unsigned long **mret, char *cret)
464 register unsigned long count = 0;
465 static char *proto[] =
466 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
471 if (!cret) return -2;
472 if (!mret) return -2;
473 if (*mret) return -2;
474 if (which < 0 || which > 6) return -2;
477 sprintf(aaa, "MSGS %s||%d", proto[which],
478 (mtemplate) ? 1 : 0);
480 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
481 (mtemplate) ? 1 : 0);
482 if (mtemplate) count = strlen(mtemplate);
483 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
487 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
490 while (bbb && strlen(bbb)) {
491 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
492 remove_token(bbb, 0, '\n');
493 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
494 sizeof (unsigned long)));
496 (*mret)[count++] = atol(aaa);
508 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
509 struct ctdlipcmessage **mret, char *cret)
515 int multipart_hunting = 0;
516 char multipart_prefix[128];
519 if (!cret) return -1;
520 if (!mret) return -1;
521 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
522 if (!*mret) return -1;
523 if (!msgnum) return -1;
525 strcpy(encoding, "");
526 strcpy(mret[0]->content_type, "");
527 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
528 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
529 if (ret / 100 == 1) {
531 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
532 while (strlen(bbb) > 4 && bbb[4] == '=') {
533 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
534 remove_token(bbb, 0, '\n');
536 if (!strncasecmp(aaa, "nhdr=yes", 8))
538 else if (!strncasecmp(aaa, "from=", 5))
539 safestrncpy(mret[0]->author, &aaa[5], SIZ);
540 else if (!strncasecmp(aaa, "type=", 5))
541 mret[0]->type = atoi(&aaa[5]);
542 else if (!strncasecmp(aaa, "msgn=", 5))
543 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
544 else if (!strncasecmp(aaa, "subj=", 5))
545 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
546 else if (!strncasecmp(aaa, "rfca=", 5))
547 safestrncpy(mret[0]->email, &aaa[5], SIZ);
548 else if (!strncasecmp(aaa, "hnod=", 5))
549 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
550 else if (!strncasecmp(aaa, "room=", 5))
551 safestrncpy(mret[0]->room, &aaa[5], SIZ);
552 else if (!strncasecmp(aaa, "node=", 5))
553 safestrncpy(mret[0]->node, &aaa[5], SIZ);
554 else if (!strncasecmp(aaa, "rcpt=", 5))
555 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
556 else if (!strncasecmp(aaa, "wefw=", 5))
557 safestrncpy(mret[0]->references, &aaa[5], SIZ);
558 else if (!strncasecmp(aaa, "time=", 5))
559 mret[0]->time = atol(&aaa[5]);
561 /* Multipart/alternative prefix & suffix strings help
562 * us to determine which part we want to download.
564 else if (!strncasecmp(aaa, "pref=", 5)) {
565 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
566 if (!strcasecmp(multipart_prefix,
567 "multipart/alternative")) {
571 else if (!strncasecmp(aaa, "suff=", 5)) {
572 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
573 if (!strcasecmp(multipart_prefix,
574 "multipart/alternative")) {
579 else if (!strncasecmp(aaa, "part=", 5)) {
580 struct parts *ptr, *chain;
582 ptr = (struct parts *)calloc(1, sizeof (struct parts));
585 /* Fill the buffers for the caller */
586 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
587 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
588 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
589 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
590 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
591 ptr->length = extract_long(&aaa[5], 5);
592 if (!mret[0]->attachments)
593 mret[0]->attachments = ptr;
595 chain = mret[0]->attachments;
601 /* Now handle multipart/alternative */
602 if (multipart_hunting > 0) {
603 if ( (!strcasecmp(ptr->mimetype,
605 || (!strcasecmp(ptr->mimetype,
607 strcpy(mret[0]->mime_chosen,
615 /* Eliminate "text\n" */
616 remove_token(bbb, 0, '\n');
618 /* If doing a MIME thing, pull out the extra headers */
621 if (!strncasecmp(bbb, "Content-type:", 13)) {
622 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
623 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
624 striplt(mret[0]->content_type);
626 /* strip out ";charset=" portion. FIXME do something with
627 * the charset (like... convert it) instead of just throwing
630 if (strstr(mret[0]->content_type, ";") != NULL) {
631 strcpy(strstr(mret[0]->content_type, ";"), "");
635 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
636 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
637 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
638 striplt(mret[0]->mime_chosen);
640 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
641 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
642 strcpy(encoding, &encoding[26]);
645 remove_token(bbb, 0, '\n');
646 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
647 remove_token(bbb, 0, '\n');
654 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
656 int bytes_decoded = 0;
657 ccc = malloc(strlen(bbb) + 32768);
658 if (!strcasecmp(encoding, "base64")) {
659 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
661 else if (!strcasecmp(encoding, "quoted-printable")) {
662 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
664 ccc[bytes_decoded] = 0;
669 /* FIXME: Strip trailing whitespace */
670 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
673 bbb = (char *)realloc(bbb, 1);
683 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
688 if (!cret) return -2;
689 if (!listing) return -2;
690 if (*listing) return -2;
692 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
698 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
702 char *listing = NULL;
705 if (!cret) return -2;
707 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
708 if (ret / 100 == 1) {
711 while (*listing && strlen(listing)) {
712 extract_token(buf, listing, 0, '\n', sizeof buf);
713 remove_token(listing, 0, '\n');
715 case 0: ipc->ServInfo.pid = atoi(buf);
717 case 1: strcpy(ipc->ServInfo.nodename,buf);
719 case 2: strcpy(ipc->ServInfo.humannode,buf);
721 case 3: strcpy(ipc->ServInfo.fqdn,buf);
723 case 4: strcpy(ipc->ServInfo.software,buf);
725 case 5: ipc->ServInfo.rev_level = atoi(buf);
727 case 6: strcpy(ipc->ServInfo.site_location,buf);
729 case 7: strcpy(ipc->ServInfo.sysadm,buf);
731 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
733 case 10: ipc->ServInfo.ok_floors = atoi(buf);
735 case 11: ipc->ServInfo.paging_level = atoi(buf);
737 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
739 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
741 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
743 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
745 case 17: ipc->ServInfo.load_avg = atof(buf);
747 case 18: ipc->ServInfo.worker_avg = atof(buf);
749 case 19: ipc->ServInfo.thread_count = atoi(buf);
751 case 20: ipc->ServInfo.has_sieve = atoi(buf);
753 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
755 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
757 case 24: ipc->ServInfo.guest_logins = atoi(buf);
763 if (listing) free(listing);
769 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
774 if (!cret) return -2;
775 if (!listing) return -2;
776 if (*listing) return -2;
778 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
784 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
786 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
791 if (!cret) return -2;
794 sprintf(aaa, "SLRP %ld", msgnum);
797 sprintf(aaa, "SLRP HIGHEST");
799 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
805 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
810 if (!cret) return -2;
811 if (!username) return -2;
813 aaa = (char *)malloc(strlen(username) + 6);
816 sprintf(aaa, "INVT %s", username);
817 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
824 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
829 if (!cret) return -1;
830 if (!username) return -1;
832 aaa = (char *)malloc(strlen(username) + 6);
834 sprintf(aaa, "KICK %s", username);
835 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
842 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
846 if (!cret) return -2;
847 if (!qret) return -2;
848 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
849 if (!*qret) return -1;
851 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
852 if (ret / 100 == 2) {
853 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
854 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
855 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
856 qret[0]->QRflags = extract_int(cret, 3);
857 qret[0]->QRfloor = extract_int(cret, 4);
858 qret[0]->QRorder = extract_int(cret, 5);
859 qret[0]->QRdefaultview = extract_int(cret, 6);
860 qret[0]->QRflags2 = extract_int(cret, 7);
867 /* set forget to kick all users out of room */
868 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
873 if (!cret) return -2;
874 if (!qret) return -2;
876 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
877 strlen(qret->QRdirname) + 64);
880 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
881 qret->QRname, qret->QRpasswd, qret->QRdirname,
882 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
883 qret->QRdefaultview, qret->QRflags2);
884 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
891 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
893 if (!cret) return -1;
895 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
900 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
905 if (!cret) return -2;
906 if (!username) return -2;
908 aaa = (char *)malloc(strlen(username) + 6);
911 sprintf(aaa, "SETA %s", username);
912 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
919 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
925 if (!cret) return -2;
928 if (mr->references) {
929 for (ptr=mr->references; *ptr != 0; ++ptr) {
930 if (*ptr == '|') *ptr = '!';
934 snprintf(cmd, sizeof cmd,
935 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
936 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
937 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
939 if ((flag == 0) && (subject_required != NULL)) {
940 /* Is the server strongly recommending that the user enter a message subject? */
941 if ((cret[3] != '\0') && (cret[4] != '\0')) {
942 *subject_required = extract_int(&cret[4], 1);
952 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
956 if (!cret) return -2;
957 if (!iret) return -2;
958 if (*iret) return -2;
960 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
965 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
969 if (!cret) return -2;
970 if (!msgnum) return -2;
972 sprintf(aaa, "DELE %ld", msgnum);
973 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
978 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
983 if (!cret) return -2;
984 if (!destroom) return -2;
985 if (!msgnum) return -2;
987 aaa = (char *)malloc(strlen(destroom) + 28);
990 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
991 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
998 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
1002 if (!cret) return -2;
1004 sprintf(aaa, "KILL %d", for_real);
1005 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1010 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
1011 const char *password, int floor, char *cret)
1016 if (!cret) return -2;
1017 if (!roomname) return -2;
1020 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
1021 if (!aaa) return -1;
1022 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
1025 aaa = (char *)malloc(strlen(roomname) + 40);
1026 if (!aaa) return -1;
1027 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1030 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1037 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1039 if (!cret) return -2;
1041 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1046 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1052 if (!cret) return -2;
1053 if (!mret) return -2;
1054 if (*mret) return -2;
1055 if (!message) return -2;
1057 aaa = (char *)malloc(strlen(message) + 6);
1058 if (!aaa) return -1;
1060 sprintf(aaa, "MESG %s", message);
1061 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1068 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1070 if (!cret) return -2;
1072 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1077 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1083 if (!cret) return -2;
1084 if (!rret) return -2;
1085 if (*rret) return -2;
1088 aaa = (char *)malloc(strlen(username) + 6);
1090 aaa = (char *)malloc(12);
1091 if (!aaa) return -1;
1094 sprintf(aaa, "GREG %s", username);
1096 sprintf(aaa, "GREG _SELF_");
1097 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1104 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1109 if (!cret) return -2;
1110 if (!username) return -2;
1111 if (axlevel < AxDeleted || axlevel > AxAideU) return -2;
1113 aaa = (char *)malloc(strlen(username) + 17);
1114 if (!aaa) return -1;
1116 sprintf(aaa, "VALI %s|%d", username, axlevel);
1117 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1124 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1128 if (!cret) return -1;
1129 if (!info) return -1;
1131 sprintf(aaa, "EINF %d", for_real);
1132 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1137 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1143 if (!cret) return -1;
1144 if (!listing) return -1;
1145 if (*listing) return -1;
1146 if (!searchstring) return -1;
1148 cmd = malloc(strlen(searchstring) + 10);
1149 sprintf(cmd, "LIST %s", searchstring);
1151 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1158 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1160 if (!cret) return -1;
1161 if (!info) return -1;
1163 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1169 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1173 if (!cret) return -1;
1174 if (!chek) return -1;
1176 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1177 if (ret / 100 == 2) {
1178 chek->newmail = extract_long(cret, 0);
1179 chek->needregis = extract_int(cret, 1);
1180 chek->needvalid = extract_int(cret, 2);
1187 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1192 if (!cret) return -2;
1193 if (!filename) return -2;
1195 aaa = (char *)malloc(strlen(filename) + 6);
1196 if (!aaa) return -1;
1198 sprintf(aaa, "DELF %s", filename);
1199 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1206 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1211 if (!cret) return -2;
1212 if (!filename) return -2;
1213 if (!destroom) return -2;
1215 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1216 if (!aaa) return -1;
1218 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1219 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1226 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1231 if (!cret) return -1;
1232 if (!listing) return -1;
1233 if (*listing) return -1;
1235 *stamp = CtdlIPCServerTime(ipc, cret);
1237 *stamp = time(NULL);
1238 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1244 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1246 void (*progress_gauge_callback)
1247 (CtdlIPC*, unsigned long, unsigned long),
1256 if (!cret) return -2;
1257 if (!filename) return -2;
1258 if (!buf) return -2;
1259 if (*buf) return -2;
1260 if (ipc->downloading) return -2;
1262 aaa = (char *)malloc(strlen(filename) + 6);
1263 if (!aaa) return -1;
1265 sprintf(aaa, "OPEN %s", filename);
1266 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1268 if (ret / 100 == 2) {
1269 ipc->downloading = 1;
1270 bytes = extract_long(cret, 0);
1271 last_mod = extract_int(cret, 1);
1272 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1274 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1275 progress_gauge_callback, cret);
1277 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1278 progress_gauge_callback, cret);
1281 ret = CtdlIPCEndDownload(ipc, cret);
1283 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1284 filename, mimetype);
1291 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1293 void (*progress_gauge_callback)
1294 (CtdlIPC*, unsigned long, unsigned long),
1304 if (!cret) return -2;
1305 if (!buf) return -2;
1306 if (*buf) return -2;
1307 if (!part) return -2;
1308 if (!msgnum) return -2;
1309 if (ipc->downloading) return -2;
1311 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1312 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1313 if (ret / 100 == 2) {
1314 ipc->downloading = 1;
1315 bytes = extract_long(cret, 0);
1316 last_mod = extract_int(cret, 1);
1317 extract_token(filename, cret, 2, '|', sizeof filename);
1318 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1319 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1320 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1321 ret = CtdlIPCEndDownload(ipc, cret);
1323 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1324 filename, mimetype);
1331 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1332 void (*progress_gauge_callback)
1333 (CtdlIPC*, unsigned long, unsigned long),
1342 if (!cret) return -1;
1343 if (!buf) return -1;
1344 if (*buf) return -1;
1345 if (!filename) return -1;
1346 if (ipc->downloading) return -1;
1348 aaa = (char *)malloc(strlen(filename) + 6);
1349 if (!aaa) return -1;
1351 sprintf(aaa, "OIMG %s", filename);
1352 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1354 if (ret / 100 == 2) {
1355 ipc->downloading = 1;
1356 bytes = extract_long(cret, 0);
1357 last_mod = extract_int(cret, 1);
1358 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1359 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1360 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1361 ret = CtdlIPCEndDownload(ipc, cret);
1363 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1364 filename, mimetype);
1371 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1373 void (*progress_gauge_callback)
1374 (CtdlIPC*, unsigned long, unsigned long),
1380 char MimeTestBuf[64];
1381 const char *MimeType;
1384 if (!cret) return -1;
1385 if (!save_as) return -1;
1386 if (!comment) return -1;
1387 if (!path) return -1;
1388 if (!*path) return -1;
1389 if (ipc->uploading) return -1;
1391 uploadFP = fopen(path, "r");
1392 if (!uploadFP) return -2;
1394 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1399 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1400 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1401 if (!aaa) return -1;
1403 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1404 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1406 if (ret / 100 == 2) {
1408 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1409 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1417 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1418 const char *save_as,
1419 void (*progress_gauge_callback)
1420 (CtdlIPC*, unsigned long, unsigned long),
1426 char MimeTestBuf[64];
1427 const char *MimeType;
1430 if (!cret) return -1;
1431 if (!save_as) return -1;
1432 if (!path && for_real) return -1;
1433 if (!*path && for_real) return -1;
1434 if (ipc->uploading) return -1;
1436 aaa = (char *)malloc(strlen(save_as) + 17);
1437 if (!aaa) return -1;
1439 uploadFP = fopen(path, "r");
1440 if (!uploadFP) return -2;
1442 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1446 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1448 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1449 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1451 if (ret / 100 == 2 && for_real) {
1453 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1454 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1462 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1467 if (!cret) return -2;
1468 if (!username) return -2;
1470 aaa = (char *)malloc(strlen(username) + 6);
1471 if (!aaa) return -1;
1473 sprintf(aaa, "QUSR %s", username);
1474 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1481 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1485 if (!cret) return -2;
1486 if (!listing) return -2;
1487 if (*listing) return -2;
1489 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1494 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1499 if (!cret) return -2;
1500 if (!name) return -2;
1502 sprintf(aaa, "CFLR %s|%d", name, for_real);
1503 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1509 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1513 if (!cret) return -1;
1514 if (floornum < 0) return -1;
1516 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1517 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1522 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1527 if (!cret) return -2;
1528 if (!floorname) return -2;
1529 if (floornum < 0) return -2;
1531 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1532 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1540 * You only need to fill out hostname, the defaults will be used if any of the
1541 * other fields are not set properly.
1543 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1544 int revision, const char *software_name, const char *hostname,
1550 if (developerid < 0 || clientid < 0 || revision < 0 ||
1554 revision = REV_LEVEL - 600;
1555 software_name = "Citadel (libcitadel)";
1557 if (!hostname) return -2;
1559 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1560 if (!aaa) return -1;
1562 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1563 revision, software_name, hostname);
1564 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1571 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1577 if (!cret) return -2;
1578 if (!username) return -2;
1580 aaa = (char *)malloc(strlen(username) + 8);
1581 if (!aaa) return -1;
1584 sprintf(aaa, "SEXP %s|-", username);
1585 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1588 sprintf(aaa, "SEXP %s||", username);
1589 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1597 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1601 if (!cret) return -2;
1602 if (!listing) return -2;
1603 if (*listing) return -2;
1605 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1610 /* mode is 0 = enable, 1 = disable, 2 = status */
1611 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1615 if (!cret) return -2;
1617 sprintf(aaa, "DEXP %d", mode);
1618 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1623 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1625 if (!cret) return -2;
1626 if (!bio) return -2;
1628 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1634 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1640 if (!cret) return -2;
1641 if (!username) return -2;
1642 if (!listing) return -2;
1643 if (*listing) return -2;
1645 aaa = (char *)malloc(strlen(username) + 6);
1646 if (!aaa) return -1;
1648 sprintf(aaa, "RBIO %s", username);
1649 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1656 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1660 if (!cret) return -2;
1661 if (!listing) return -2;
1662 if (*listing) return -2;
1664 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1669 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1673 if (!cret) return -1;
1675 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1676 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1681 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1685 if (!cret) return -1;
1687 sprintf(aaa, "TERM %d", sid);
1688 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1693 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1695 if (!cret) return -1;
1697 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1702 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1706 if (!cret) return -1;
1708 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1709 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1714 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1720 if (!cret) return -2;
1721 if (!text) return -2;
1722 if (!filename) return -2;
1724 aaa = (char *)malloc(strlen(filename) + 6);
1725 if (!aaa) return -1;
1727 sprintf(aaa, "EMSG %s", filename);
1728 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1735 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1740 if (!cret) return -2;
1741 if (!hostname) return -2;
1743 aaa = (char *)malloc(strlen(hostname) + 6);
1744 if (!aaa) return -1;
1746 sprintf(aaa, "HCHG %s", hostname);
1747 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1754 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1759 if (!cret) return -2;
1760 if (!roomname) return -2;
1762 aaa = (char *)malloc(strlen(roomname) + 6);
1763 if (!aaa) return -1;
1765 sprintf(aaa, "RCHG %s", roomname);
1766 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1773 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1778 if (!cret) return -2;
1779 if (!username) return -2;
1781 aaa = (char *)malloc(strlen(username) + 6);
1782 if (!aaa) return -1;
1784 sprintf(aaa, "UCHG %s", username);
1785 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1792 /* This function returns the actual server time reported, or 0 if error */
1793 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1795 register time_t tret;
1798 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1799 if (ret / 100 == 2) {
1800 tret = extract_long(cret, 0);
1809 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1810 struct ctdluser **uret, char *cret)
1815 if (!cret) return -2;
1816 if (!uret) return -2;
1817 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1818 if (!*uret) return -1;
1820 sprintf(aaa, "AGUP %s", who);
1821 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1823 if (ret / 100 == 2) {
1824 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1825 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1826 uret[0]->flags = extract_int(cret, 2);
1827 uret[0]->timescalled = extract_long(cret, 3);
1828 uret[0]->posted = extract_long(cret, 4);
1829 uret[0]->axlevel = extract_int(cret, 5);
1830 uret[0]->usernum = extract_long(cret, 6);
1831 uret[0]->lastcall = extract_long(cret, 7);
1832 uret[0]->USuserpurge = extract_int(cret, 8);
1839 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1844 if (!cret) return -2;
1845 if (!uret) return -2;
1847 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1848 if (!aaa) return -1;
1850 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1851 uret->fullname, uret->password, uret->flags,
1852 uret->timescalled, uret->posted, uret->axlevel,
1853 uret->usernum, uret->lastcall, uret->USuserpurge);
1854 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1861 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1862 /* caller must free the struct ExpirePolicy */
1863 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
1864 struct ExpirePolicy **policy, char *cret)
1866 static char *proto[] = {
1870 strof(mailboxespolicy)
1875 if (!cret) return -2;
1876 if (!policy) return -2;
1877 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1878 if (!*policy) return -1;
1879 if (which < 0 || which > 3) return -2;
1881 sprintf(cmd, "GPEX %s", proto[which]);
1882 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1883 if (ret / 100 == 2) {
1884 policy[0]->expire_mode = extract_int(cret, 0);
1885 policy[0]->expire_value = extract_int(cret, 1);
1892 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1893 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1894 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1895 struct ExpirePolicy *policy, char *cret)
1898 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1900 if (!cret) return -2;
1901 if (which < 0 || which > 3) return -2;
1902 if (!policy) return -2;
1903 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1904 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1906 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1907 policy->expire_mode, policy->expire_value);
1908 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1913 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1917 if (!cret) return -2;
1918 if (!listing) return -2;
1919 if (*listing) return -2;
1921 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1922 listing, &bytes, cret);
1927 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1929 if (!cret) return -2;
1930 if (!listing) return -2;
1932 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1938 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1939 char **listing, char *cret)
1945 if (!cret) return -2;
1946 if (!mimetype) return -2;
1947 if (!listing) return -2;
1948 if (*listing) return -2;
1950 aaa = malloc(strlen(mimetype) + 13);
1951 if (!aaa) return -1;
1952 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1953 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1954 listing, &bytes, cret);
1961 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1962 const char *listing, char *cret)
1967 if (!cret) return -2;
1968 if (!mimetype) return -2;
1969 if (!listing) return -2;
1971 aaa = malloc(strlen(mimetype) + 13);
1972 if (!aaa) return -1;
1973 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1974 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1982 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1986 if (!cret) return -2;
1987 if (!listing) return -2;
1988 if (*listing) return -2;
1990 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1991 listing, &bytes, cret);
1996 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1998 if (!cret) return -2;
1999 if (!listing) return -2;
2001 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
2007 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2011 if (!cret) return -2;
2012 if (session < 0) return -2;
2014 sprintf(aaa, "REQT %d", session);
2015 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2020 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2024 if (!cret) return -2;
2025 if (msgnum < 0) return -2;
2027 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2028 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2033 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2042 /* New SSL object */
2043 temp_ssl = SSL_new(ssl_ctx);
2045 error_printf("SSL_new failed: %s\n",
2046 ERR_reason_error_string(ERR_get_error()));
2049 /* Pointless flag waving */
2050 #if SSLEAY_VERSION_NUMBER >= 0x0922
2051 SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
2054 if (!access(EGD_POOL, F_OK))
2057 if (!RAND_status()) {
2058 error_printf("PRNG not properly seeded\n");
2062 /* Associate network connection with SSL object */
2063 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2064 error_printf("SSL_set_fd failed: %s\n",
2065 ERR_reason_error_string(ERR_get_error()));
2069 if (status_hook != NULL)
2070 status_hook("Requesting encryption...\r");
2072 /* Ready to start SSL/TLS */
2074 CtdlIPC_putline(ipc, "STLS");
2075 CtdlIPC_getline(ipc, buf);
2076 if (buf[0] != '2') {
2077 error_printf("Server can't start TLS: %s\n", buf);
2081 r = CtdlIPCGenericCommand(ipc,
2082 "STLS", NULL, 0, NULL, NULL, cret);
2084 error_printf("Server can't start TLS: %s\n", buf);
2089 /* Do SSL/TLS handshake */
2090 if ((a = SSL_connect(temp_ssl)) < 1) {
2091 error_printf("SSL_connect failed: %s\n",
2092 ERR_reason_error_string(ERR_get_error()));
2096 ipc->ssl = temp_ssl;
2098 if (BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE))
2102 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2103 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2104 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2105 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2111 #endif /* HAVE_OPENSSL */
2116 static void endtls(SSL *ssl)
2127 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2132 if (!address) return -2;
2133 if (!cret) return -2;
2135 aaa = (char *)malloc(strlen(address) + 6);
2136 if (!aaa) return -1;
2138 sprintf(aaa, "QDIR %s", address);
2139 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2146 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2150 if (!cret) return -2;
2151 sprintf(aaa, "IPGM %d", secret);
2152 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2157 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2161 if (!cret) return -2;
2162 if (!mret) return -2;
2163 if (*mret) return -2;
2165 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2186 /* ************************************************************************** */
2187 /* Stuff below this line is not for public consumption */
2188 /* ************************************************************************** */
2191 /* Read a listing from the server up to 000. Append to dest if it exists */
2192 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2201 length = strlen(ret);
2206 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2207 linelength = strlen(aaa);
2208 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2210 strcpy(&ret[length], aaa);
2211 length += linelength;
2212 strcpy(&ret[length++], "\n");
2220 /* Send a listing to the server; generate the ending 000. */
2221 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2225 text = (char *)malloc(strlen(listing) + 6);
2227 strcpy(text, listing);
2228 while (text[strlen(text) - 1] == '\n')
2229 text[strlen(text) - 1] = '\0';
2230 strcat(text, "\n000");
2231 CtdlIPC_putline(ipc, text);
2235 /* Malloc failed but we are committed to send */
2236 /* This may result in extra blanks at the bottom */
2237 CtdlIPC_putline(ipc, text);
2238 CtdlIPC_putline(ipc, "000");
2244 /* Partial read of file from server */
2245 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2247 register size_t len = 0;
2251 if (!cret) return 0;
2252 if (bytes < 1) return 0;
2255 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2256 CtdlIPC_putline(ipc, aaa);
2257 CtdlIPC_getline(ipc, aaa);
2259 strcpy(cret, &aaa[4]);
2261 len = extract_long(&aaa[4], 0);
2262 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2264 /* I know what I'm doing */
2265 serv_read(ipc, ((char *)(*buf) + offset), len);
2267 /* We have to read regardless */
2268 serv_read(ipc, aaa, len);
2272 CtdlIPC_unlock(ipc);
2278 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2282 if (!cret) return -2;
2283 if (!ipc->downloading) return -2;
2285 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2287 ipc->downloading = 0;
2293 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2297 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2298 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2305 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2306 void (*progress_gauge_callback)
2307 (CtdlIPC*, unsigned long, unsigned long),
2310 register size_t len;
2312 if (!cret) return -1;
2313 if (!buf) return -1;
2314 if (*buf) return -1;
2315 if (!ipc->downloading) return -1;
2318 if (progress_gauge_callback)
2319 progress_gauge_callback(ipc, len, bytes);
2320 while (len < bytes) {
2321 register size_t block;
2323 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2329 if (progress_gauge_callback)
2330 progress_gauge_callback(ipc, len, bytes);
2335 /* READ - pipelined */
2336 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2338 void (*progress_gauge_callback)
2339 (CtdlIPC*, unsigned long, unsigned long),
2342 register size_t len;
2343 register int calls; /* How many calls in the pipeline */
2344 register int i; /* iterator */
2347 if (!cret) return -1;
2348 if (!buf) return -1;
2349 if (*buf) return -1;
2350 if (!ipc->downloading) return -1;
2352 *buf = (void *)realloc(*buf, bytes - resume);
2353 if (!*buf) return -1;
2357 if (progress_gauge_callback)
2358 progress_gauge_callback(ipc, len, bytes);
2360 /* How many calls will be in the pipeline? */
2361 calls = (bytes - resume) / 4096;
2362 if ((bytes - resume) % 4096) calls++;
2364 /* Send all requests at once */
2365 for (i = 0; i < calls; i++) {
2366 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2367 CtdlIPC_putline(ipc, aaa);
2370 /* Receive all responses at once */
2371 for (i = 0; i < calls; i++) {
2372 CtdlIPC_getline(ipc, aaa);
2374 strcpy(cret, &aaa[4]);
2376 len = extract_long(&aaa[4], 0);
2377 /* I know what I'm doing */
2378 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2380 if (progress_gauge_callback)
2381 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2383 CtdlIPC_unlock(ipc);
2389 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2394 if (!cret) return -1;
2395 if (!ipc->uploading) return -1;
2397 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2398 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2405 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2406 void (*progress_gauge_callback)
2407 (CtdlIPC*, unsigned long, unsigned long),
2410 register int ret = -1;
2411 register size_t offset = 0;
2415 FILE *fd = uploadFP;
2418 if (!cret) return -1;
2420 fseek(fd, 0L, SEEK_END);
2424 if (progress_gauge_callback)
2425 progress_gauge_callback(ipc, 0, bytes);
2427 while (offset < bytes) {
2428 register size_t to_write;
2430 /* Read some data in */
2431 to_write = fread(buf, 1, 4096, fd);
2433 if (feof(fd) || ferror(fd)) break;
2435 sprintf(aaa, "WRIT %d", (int)to_write);
2436 CtdlIPC_putline(ipc, aaa);
2437 CtdlIPC_getline(ipc, aaa);
2438 strcpy(cret, &aaa[4]);
2440 if (aaa[0] == '7') {
2441 to_write = extract_long(&aaa[4], 0);
2443 serv_write(ipc, buf, to_write);
2445 if (progress_gauge_callback)
2446 progress_gauge_callback(ipc, offset, bytes);
2447 /* Detect short reads and back up if needed */
2448 /* offset will never be negative anyway */
2449 fseek(fd, (signed)offset, SEEK_SET);
2454 if (progress_gauge_callback)
2455 progress_gauge_callback(ipc, 1, 1);
2458 return (!ferr ? ret : -2);
2463 * Generic command method. This method should handle any server command
2464 * except for CHAT. It takes the following arguments:
2466 * ipc The server to speak with
2467 * command Preformatted command to send to server
2468 * to_send A text or binary file to send to server
2469 * (only sent if server requests it)
2470 * bytes_to_send The number of bytes in to_send (required if
2471 * sending binary, optional if sending listing)
2472 * to_receive Pointer to a NULL pointer, if the server
2473 * sends text or binary we will allocate memory
2474 * for the file and stuff it here
2475 * bytes_to_receive If a file is received, we will store its
2477 * proto_response The protocol response. Caller must provide
2478 * this buffer and ensure that it is at least
2479 * 128 bytes in length.
2481 * This function returns a number equal to the protocol response number,
2482 * -1 if an internal error occurred, -2 if caller provided bad values,
2483 * or 0 - the protocol response number if bad values were found during
2484 * the protocol exchange.
2485 * It stores the protocol response string (minus the number) in
2486 * protocol_response as described above. Some commands send additional
2487 * data in this string.
2489 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2490 const char *command, const char *to_send,
2491 size_t bytes_to_send, char **to_receive,
2492 size_t *bytes_to_receive, char *proto_response)
2498 if (!command) return -2;
2499 if (!proto_response) return -2;
2502 if (ipc->ssl) watch_ssl = 1;
2506 CtdlIPC_putline(ipc, command);
2508 CtdlIPC_getline(ipc, proto_response);
2509 if (proto_response[3] == '*')
2511 ret = atoi(proto_response);
2512 strcpy(proto_response, &proto_response[4]);
2513 switch (ret / 100) {
2514 default: /* Unknown, punt */
2516 case 3: /* MORE_DATA */
2518 /* Don't need to do anything */
2520 case 1: /* LISTING_FOLLOWS */
2521 if (to_receive && !*to_receive && bytes_to_receive) {
2522 *to_receive = CtdlIPCReadListing(ipc, NULL);
2523 } else { /* Drain */
2524 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2528 case 4: /* SEND_LISTING */
2530 CtdlIPCSendListing(ipc, to_send);
2532 /* No listing given, fake it */
2533 CtdlIPC_putline(ipc, "000");
2537 case 6: /* BINARY_FOLLOWS */
2538 if (to_receive && !*to_receive && bytes_to_receive) {
2540 extract_long(proto_response, 0);
2541 *to_receive = (char *)
2542 malloc((size_t)*bytes_to_receive);
2546 serv_read(ipc, *to_receive,
2553 drain = extract_long(proto_response, 0);
2554 while (drain > SIZ) {
2555 serv_read(ipc, buf, SIZ);
2558 serv_read(ipc, buf, drain);
2562 case 7: /* SEND_BINARY */
2563 if (to_send && bytes_to_send) {
2564 serv_write(ipc, to_send, bytes_to_send);
2565 } else if (bytes_to_send) {
2566 /* Fake it, send nulls */
2569 fake = bytes_to_send;
2570 memset(buf, '\0', SIZ);
2571 while (fake > SIZ) {
2572 serv_write(ipc, buf, SIZ);
2575 serv_write(ipc, buf, fake);
2577 } /* else who knows? DANGER WILL ROBINSON */
2579 case 8: /* START_CHAT_MODE */
2580 if (!strncasecmp(command, "CHAT", 4)) {
2581 /* Don't call chatmode with generic! */
2582 CtdlIPC_putline(ipc, "/quit");
2585 /* In this mode we send then receive listing */
2587 CtdlIPCSendListing(ipc, to_send);
2589 /* No listing given, fake it */
2590 CtdlIPC_putline(ipc, "000");
2593 if (to_receive && !*to_receive
2594 && bytes_to_receive) {
2595 *to_receive = CtdlIPCReadListing(ipc, NULL);
2596 } else { /* Drain */
2597 while (CtdlIPC_getline(ipc, buf),
2598 strcmp(buf, "000")) ;
2603 case 9: /* ASYNC_MSG */
2604 /* CtdlIPCDoAsync(ret, proto_response); */
2605 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2611 CtdlIPC_unlock(ipc);
2617 * Connect to a Citadel on a remote host using a TCP/IP socket
2619 static int tcp_connectsock(char *host, char *service)
2621 struct in6_addr serveraddr;
2622 struct addrinfo hints;
2623 struct addrinfo *res = NULL;
2624 struct addrinfo *ai = NULL;
2628 if ((host == NULL) || IsEmptyStr(host)) {
2629 service = DEFAULT_HOST ;
2631 if ((service == NULL) || IsEmptyStr(service)) {
2632 service = DEFAULT_PORT ;
2635 memset(&hints, 0x00, sizeof(hints));
2636 hints.ai_flags = AI_NUMERICSERV;
2637 hints.ai_family = AF_UNSPEC;
2638 hints.ai_socktype = SOCK_STREAM;
2641 * Handle numeric IPv4 and IPv6 addresses
2643 rc = inet_pton(AF_INET, host, &serveraddr);
2644 if (rc == 1) { /* dotted quad */
2645 hints.ai_family = AF_INET;
2646 hints.ai_flags |= AI_NUMERICHOST;
2649 rc = inet_pton(AF_INET6, host, &serveraddr);
2650 if (rc == 1) { /* IPv6 address */
2651 hints.ai_family = AF_INET6;
2652 hints.ai_flags |= AI_NUMERICHOST;
2656 /* Begin the connection process */
2658 rc = getaddrinfo(host, service, &hints, &res);
2664 * Try all available addresses until we connect to one or until we run out.
2666 for (ai = res; ai != NULL; ai = ai->ai_next) {
2667 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2668 if (sock < 0) return(-1);
2670 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2672 return(sock); /* Connected! */
2675 close(sock); /* Failed. Close the socket to avoid fd leak! */
2687 * Connect to a Citadel on the local host using a unix domain socket
2689 static int uds_connectsock(int *isLocal, char *sockpath)
2691 struct sockaddr_un addr;
2694 memset(&addr, 0, sizeof(addr));
2695 addr.sun_family = AF_UNIX;
2696 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2698 s = socket(AF_UNIX, SOCK_STREAM, 0);
2703 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2714 * input binary data from socket
2716 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2718 unsigned int len, rlen;
2720 #if defined(HAVE_OPENSSL)
2722 serv_read_ssl(ipc, buf, bytes);
2727 while (len < bytes) {
2728 rlen = read(ipc->sock, &buf[len], bytes - len);
2730 connection_died(ipc, 0);
2739 * send binary to server
2741 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2743 unsigned int bytes_written = 0;
2746 #if defined(HAVE_OPENSSL)
2748 serv_write_ssl(ipc, buf, nbytes);
2752 while (bytes_written < nbytes) {
2753 retval = write(ipc->sock, &buf[bytes_written],
2754 nbytes - bytes_written);
2756 connection_died(ipc, 0);
2759 bytes_written += retval;
2766 * input binary data from encrypted connection
2768 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2774 while (len < bytes) {
2775 if (SSL_want_read(ipc->ssl)) {
2776 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2777 error_printf("SSL_write in serv_read:\n");
2778 ERR_print_errors_fp(stderr);
2781 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2785 errval = SSL_get_error(ipc->ssl, rlen);
2786 if (errval == SSL_ERROR_WANT_READ ||
2787 errval == SSL_ERROR_WANT_WRITE) {
2792 Not sure why we'd want to handle these error codes any differently,
2793 but this definitely isn't the way to handle them. Someone must have
2794 naively assumed that we could fall back to unencrypted communications,
2795 but all it does is just recursively blow the stack.
2796 if (errval == SSL_ERROR_ZERO_RETURN ||
2797 errval == SSL_ERROR_SSL) {
2798 serv_read(ipc, &buf[len], bytes - len);
2802 error_printf("SSL_read in serv_read: %s\n",
2803 ERR_reason_error_string(ERR_peek_error()));
2804 connection_died(ipc, 1);
2813 * send binary to server encrypted
2815 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2817 unsigned int bytes_written = 0;
2821 while (bytes_written < nbytes) {
2822 if (SSL_want_write(ipc->ssl)) {
2823 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2824 error_printf("SSL_read in serv_write:\n");
2825 ERR_print_errors_fp(stderr);
2828 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2829 nbytes - bytes_written);
2833 errval = SSL_get_error(ipc->ssl, retval);
2834 if (errval == SSL_ERROR_WANT_READ ||
2835 errval == SSL_ERROR_WANT_WRITE) {
2839 if (errval == SSL_ERROR_ZERO_RETURN ||
2840 errval == SSL_ERROR_SSL) {
2841 serv_write(ipc, &buf[bytes_written],
2842 nbytes - bytes_written);
2845 error_printf("SSL_write in serv_write: %s\n",
2846 ERR_reason_error_string(ERR_peek_error()));
2847 connection_died(ipc, 1);
2850 bytes_written += retval;
2855 #ifdef THREADED_CLIENT
2856 static void ssl_lock(int mode, int n, const char *file, int line)
2858 if (mode & CRYPTO_LOCK)
2859 pthread_mutex_lock(Critters[n]);
2861 pthread_mutex_unlock(Critters[n]);
2863 #endif /* THREADED_CLIENT */
2866 static void CtdlIPC_init_OpenSSL(void)
2869 SSL_METHOD *ssl_method;
2872 /* already done init */
2881 SSL_load_error_strings();
2882 SSLeay_add_ssl_algorithms();
2884 /* Set up the SSL context in which we will oeprate */
2885 ssl_method = SSLv23_client_method();
2886 ssl_ctx = SSL_CTX_new(ssl_method);
2888 error_printf("SSL_CTX_new failed: %s\n",
2889 ERR_reason_error_string(ERR_get_error()));
2892 /* Any reasonable cipher we can get */
2893 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2894 error_printf("No ciphers available for encryption\n");
2897 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2899 /* Load DH parameters into the context */
2902 error_printf("Can't allocate a DH object: %s\n",
2903 ERR_reason_error_string(ERR_get_error()));
2906 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2907 error_printf("Can't assign DH_P: %s\n",
2908 ERR_reason_error_string(ERR_get_error()));
2912 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2913 error_printf("Can't assign DH_G: %s\n",
2914 ERR_reason_error_string(ERR_get_error()));
2919 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2922 #ifdef THREADED_CLIENT
2923 /* OpenSSL requires callbacks for threaded clients */
2924 CRYPTO_set_locking_callback(ssl_lock);
2925 CRYPTO_set_id_callback(id_callback);
2927 /* OpenSSL requires us to do semaphores for threaded clients */
2928 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2930 perror("malloc failed");
2933 for (a = 0; a < CRYPTO_num_locks(); a++) {
2934 Critters[a] = malloc(sizeof (pthread_mutex_t));
2936 perror("malloc failed");
2939 pthread_mutex_init(Critters[a], NULL);
2942 #endif /* THREADED_CLIENT */
2947 #ifdef THREADED_CLIENT
2948 static unsigned long id_callback(void) {
2949 return (unsigned long)pthread_self();
2951 #endif /* THREADED_CLIENT */
2952 #endif /* HAVE_OPENSSL */
2956 ReadNetworkChunk(CtdlIPC* ipc)
2973 FD_SET(ipc->sock, &read_fd);
2974 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2976 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2980 *(ipc->BufPtr) = '\0';
2981 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2982 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2984 ipc->BufPtr[n]='\0';
2992 if (!(errno == EINTR || errno == EAGAIN))
2993 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2999 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3001 ipc->BufPtr[n]='\0';
3006 connection_died(ipc, 0);
3014 * input string from socket - implemented in terms of serv_read()
3018 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3021 char *aptr, *bptr, *aeptr, *beptr;
3023 // error_printf("---\n");
3026 #if defined(HAVE_OPENSSL)
3029 /* Read one character at a time. */
3031 serv_read(ipc, &buf[i], 1);
3032 if (buf[i] == '\n' || i == (SIZ-1))
3036 /* If we got a long line, discard characters until the newline. */
3038 while (buf[i] != '\n')
3039 serv_read(ipc, &buf[i], 1);
3041 /* Strip the trailing newline (and carriage return, if present) */
3042 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3043 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3048 if (ipc->Buf == NULL)
3051 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3053 ipc->BufPtr = ipc->Buf;
3057 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3058 if (ipc->BufUsed == 0)
3059 ReadNetworkChunk(ipc);
3061 //// if (ipc->BufUsed != 0) while (1)
3067 aeptr = ipc->Buf + ipc->BufSize;
3068 while ((aptr < aeptr) &&
3072 *(bptr++) = *(aptr++);
3073 if ((*aptr == '\n') && (aptr < aeptr))
3075 /* Terminate it right, remove the line breaks */
3076 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3078 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3081 // 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);
3082 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3085 /* is there more in the buffer we need to read later? */
3086 if (ipc->Buf + ipc->BufUsed > aptr)
3093 ipc->BufPtr = ipc->Buf;
3095 // error_printf("----bla6\n");
3098 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3099 else if ((ipc->BufPtr != ipc->Buf) &&
3100 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3102 size_t NewBufSize = ipc->BufSize * 2;
3103 int delta = (ipc->BufPtr - ipc->Buf);
3106 /* if the line would end after our buffer, we should use a bigger buffer. */
3107 NewBuf = (char *)malloc (NewBufSize + 10);
3108 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3110 ipc->Buf = ipc->BufPtr = NewBuf;
3111 ipc->BufUsed -= delta;
3112 ipc->BufSize = NewBufSize;
3114 if (ReadNetworkChunk(ipc) <0)
3116 // error_printf("----bla\n");
3120 /// error_printf("----bl45761%s\nipc->BufUsed");
3122 // error_printf("----bla1\n");
3125 #else /* CHUNKED_READ */
3127 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3131 /* Read one character at a time. */
3133 serv_read(ipc, &buf[i], 1);
3134 if (buf[i] == '\n' || i == (SIZ-1))
3138 /* If we got a long line, discard characters until the newline. */
3140 while (buf[i] != '\n')
3141 serv_read(ipc, &buf[i], 1);
3143 /* Strip the trailing newline (and carriage return, if present) */
3144 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3145 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3149 #endif /* CHUNKED_READ */
3152 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3154 CtdlIPC_getline(ipc, buf);
3158 * send line to server - implemented in terms of serv_write()
3160 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3166 cmd = malloc(len + 2);
3168 /* This requires no extra memory */
3169 serv_write(ipc, buf, len);
3170 serv_write(ipc, "\n", 1);
3172 /* This is network-optimized */
3173 strncpy(cmd, buf, len);
3174 strcpy(cmd + len, "\n");
3175 serv_write(ipc, cmd, len + 1);
3179 ipc->last_command_sent = time(NULL);
3182 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3184 CtdlIPC_putline(ipc, buf);
3191 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3199 ipc = ialloc(CtdlIPC);
3203 #if defined(HAVE_OPENSSL)
3205 CtdlIPC_init_OpenSSL();
3207 #if defined(HAVE_PTHREAD_H)
3208 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3210 ipc->sock = -1; /* Not connected */
3211 ipc->isLocal = 0; /* Not local, of course! */
3212 ipc->downloading = 0;
3214 ipc->last_command_sent = 0L;
3215 ipc->network_status_cb = NULL;
3220 strcpy(cithost, DEFAULT_HOST); /* default host */
3221 strcpy(citport, DEFAULT_PORT); /* default port */
3223 /* Allow caller to supply our values (Windows) */
3224 if (hostbuf && strlen(hostbuf) > 0)
3225 strcpy(cithost, hostbuf);
3226 if (portbuf && strlen(portbuf) > 0)
3227 strcpy(citport, portbuf);
3229 /* Read host/port from command line if present */
3230 for (a = 0; a < argc; ++a) {
3233 } else if (a == 1) {
3234 strcpy(cithost, argv[a]);
3235 } else if (a == 2) {
3236 strcpy(citport, argv[a]);
3238 error_printf("%s: usage: ",argv[0]);
3239 error_printf("%s [host] [port] ",argv[0]);
3246 if ((!strcmp(cithost, "localhost"))
3247 || (!strcmp(cithost, "127.0.0.1"))) {
3251 /* If we're using a unix domain socket we can do a bunch of stuff */
3252 if (!strcmp(cithost, UDS)) {
3253 if (!strcasecmp(citport, DEFAULT_PORT)) {
3254 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3257 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3259 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3260 if (ipc->sock == -1) {
3264 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3265 if (portbuf != NULL) strcpy(portbuf, sockpath);
3266 strcpy(ipc->ip_hostname, "");
3267 strcpy(ipc->ip_address, "");
3271 ipc->sock = tcp_connectsock(cithost, citport);
3272 if (ipc->sock == -1) {
3278 /* Learn the actual network identity of the host to which we are connected */
3280 struct sockaddr_in6 clientaddr;
3281 unsigned int addrlen = sizeof(clientaddr);
3283 ipc->ip_hostname[0] = 0;
3284 ipc->ip_address[0] = 0;
3286 getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3287 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3288 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3290 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3291 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3294 /* stuff other things elsewhere */
3296 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3297 if (portbuf != NULL) strcpy(portbuf, citport);
3303 * Disconnect and delete the IPC class (destructor)
3305 void CtdlIPC_delete(CtdlIPC* ipc)
3309 SSL_shutdown(ipc->ssl);
3314 if (ipc->sock > -1) {
3315 shutdown(ipc->sock, 2); /* Close it up */
3318 if (ipc->Buf != NULL)
3327 * Disconnect and delete the IPC class (destructor)
3328 * Also NULLs out the pointer
3330 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3332 CtdlIPC_delete(*pipc);
3338 * return the file descriptor of the server socket so we can select() on it.
3340 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3343 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3350 * return one character
3352 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3355 char CtdlIPC_get(CtdlIPC* ipc)
3360 serv_read(ipc, buf, 1);