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]->USscreenwidth = extract_int(cret, 0);
367 uret[0]->USscreenheight = extract_int(cret, 1);
368 uret[0]->flags = extract_int(cret, 2);
375 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
379 if (!uret) return -2;
380 if (!cret) return -2;
382 sprintf(aaa, "SETU %d|%d|%d",
383 uret->USscreenwidth, uret->USscreenheight,
385 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
390 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
395 if (!oldname) return -2;
396 if (!newname) return -2;
397 if (!cret) return -2;
399 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
400 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
406 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
407 struct ctdlipcroom **rret, char *cret)
412 if (!cret) return -2;
413 if (!rret) return -2;
414 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
415 if (!*rret) return -1;
418 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
423 sprintf(aaa, "GOTO %s|%s", room, passwd);
425 aaa = (char *)malloc(strlen(room) + 6);
430 sprintf(aaa, "GOTO %s", room);
432 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
433 if (ret / 100 == 2) {
434 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
435 rret[0]->RRunread = extract_long(cret, 1);
436 rret[0]->RRtotal = extract_long(cret, 2);
437 rret[0]->RRinfoupdated = extract_int(cret, 3);
438 rret[0]->RRflags = extract_int(cret, 4);
439 rret[0]->RRhighest = extract_long(cret, 5);
440 rret[0]->RRlastread = extract_long(cret, 6);
441 rret[0]->RRismailbox = extract_int(cret, 7);
442 rret[0]->RRaide = extract_int(cret, 8);
443 rret[0]->RRnewmail = extract_long(cret, 9);
444 rret[0]->RRfloor = extract_int(cret, 10);
445 rret[0]->RRcurrentview = extract_int(cret, 11);
446 rret[0]->RRdefaultview = extract_int(cret, 12);
447 /* position 13 is a trash folder flag ... irrelevant in this client */
448 rret[0]->RRflags2 = extract_int(cret, 14);
459 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
460 /* whicharg is number of messages, applies to last, first, gt, lt */
461 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
462 const char *mtemplate, unsigned long **mret, char *cret)
465 register unsigned long count = 0;
466 static char *proto[] =
467 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
472 if (!cret) return -2;
473 if (!mret) return -2;
474 if (*mret) return -2;
475 if (which < 0 || which > 6) return -2;
478 sprintf(aaa, "MSGS %s||%d", proto[which],
479 (mtemplate) ? 1 : 0);
481 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
482 (mtemplate) ? 1 : 0);
483 if (mtemplate) count = strlen(mtemplate);
484 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
488 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
491 while (bbb && strlen(bbb)) {
492 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
493 remove_token(bbb, 0, '\n');
494 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
495 sizeof (unsigned long)));
497 (*mret)[count++] = atol(aaa);
509 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
510 struct ctdlipcmessage **mret, char *cret)
516 int multipart_hunting = 0;
517 char multipart_prefix[128];
520 if (!cret) return -1;
521 if (!mret) return -1;
522 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
523 if (!*mret) return -1;
524 if (!msgnum) return -1;
526 strcpy(encoding, "");
527 strcpy(mret[0]->content_type, "");
528 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
529 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
530 if (ret / 100 == 1) {
532 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
533 while (strlen(bbb) > 4 && bbb[4] == '=') {
534 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
535 remove_token(bbb, 0, '\n');
537 if (!strncasecmp(aaa, "nhdr=yes", 8))
539 else if (!strncasecmp(aaa, "from=", 5))
540 safestrncpy(mret[0]->author, &aaa[5], SIZ);
541 else if (!strncasecmp(aaa, "type=", 5))
542 mret[0]->type = atoi(&aaa[5]);
543 else if (!strncasecmp(aaa, "msgn=", 5))
544 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
545 else if (!strncasecmp(aaa, "subj=", 5))
546 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
547 else if (!strncasecmp(aaa, "rfca=", 5))
548 safestrncpy(mret[0]->email, &aaa[5], SIZ);
549 else if (!strncasecmp(aaa, "hnod=", 5))
550 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
551 else if (!strncasecmp(aaa, "room=", 5))
552 safestrncpy(mret[0]->room, &aaa[5], SIZ);
553 else if (!strncasecmp(aaa, "node=", 5))
554 safestrncpy(mret[0]->node, &aaa[5], SIZ);
555 else if (!strncasecmp(aaa, "rcpt=", 5))
556 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
557 else if (!strncasecmp(aaa, "wefw=", 5))
558 safestrncpy(mret[0]->references, &aaa[5], SIZ);
559 else if (!strncasecmp(aaa, "time=", 5))
560 mret[0]->time = atol(&aaa[5]);
562 /* Multipart/alternative prefix & suffix strings help
563 * us to determine which part we want to download.
565 else if (!strncasecmp(aaa, "pref=", 5)) {
566 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
567 if (!strcasecmp(multipart_prefix,
568 "multipart/alternative")) {
572 else if (!strncasecmp(aaa, "suff=", 5)) {
573 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
574 if (!strcasecmp(multipart_prefix,
575 "multipart/alternative")) {
580 else if (!strncasecmp(aaa, "part=", 5)) {
581 struct parts *ptr, *chain;
583 ptr = (struct parts *)calloc(1, sizeof (struct parts));
586 /* Fill the buffers for the caller */
587 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
588 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
589 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
590 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
591 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
592 ptr->length = extract_long(&aaa[5], 5);
593 if (!mret[0]->attachments)
594 mret[0]->attachments = ptr;
596 chain = mret[0]->attachments;
602 /* Now handle multipart/alternative */
603 if (multipart_hunting > 0) {
604 if ( (!strcasecmp(ptr->mimetype,
606 || (!strcasecmp(ptr->mimetype,
608 strcpy(mret[0]->mime_chosen,
616 /* Eliminate "text\n" */
617 remove_token(bbb, 0, '\n');
619 /* If doing a MIME thing, pull out the extra headers */
622 if (!strncasecmp(bbb, "Content-type:", 13)) {
623 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
624 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
625 striplt(mret[0]->content_type);
627 /* strip out ";charset=" portion. FIXME do something with
628 * the charset (like... convert it) instead of just throwing
631 if (strstr(mret[0]->content_type, ";") != NULL) {
632 strcpy(strstr(mret[0]->content_type, ";"), "");
636 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
637 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
638 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
639 striplt(mret[0]->mime_chosen);
641 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
642 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
643 strcpy(encoding, &encoding[26]);
646 remove_token(bbb, 0, '\n');
647 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
648 remove_token(bbb, 0, '\n');
655 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
657 int bytes_decoded = 0;
658 ccc = malloc(strlen(bbb) + 32768);
659 if (!strcasecmp(encoding, "base64")) {
660 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
662 else if (!strcasecmp(encoding, "quoted-printable")) {
663 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
665 ccc[bytes_decoded] = 0;
670 /* FIXME: Strip trailing whitespace */
671 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
674 bbb = (char *)realloc(bbb, 1);
684 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
689 if (!cret) return -2;
690 if (!listing) return -2;
691 if (*listing) return -2;
693 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
699 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
703 char *listing = NULL;
706 if (!cret) return -2;
708 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
709 if (ret / 100 == 1) {
712 while (*listing && strlen(listing)) {
713 extract_token(buf, listing, 0, '\n', sizeof buf);
714 remove_token(listing, 0, '\n');
716 case 0: ipc->ServInfo.pid = atoi(buf);
718 case 1: strcpy(ipc->ServInfo.nodename,buf);
720 case 2: strcpy(ipc->ServInfo.humannode,buf);
722 case 3: strcpy(ipc->ServInfo.fqdn,buf);
724 case 4: strcpy(ipc->ServInfo.software,buf);
726 case 5: ipc->ServInfo.rev_level = atoi(buf);
728 case 6: strcpy(ipc->ServInfo.site_location,buf);
730 case 7: strcpy(ipc->ServInfo.sysadm,buf);
732 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
734 case 10: ipc->ServInfo.ok_floors = atoi(buf);
736 case 11: ipc->ServInfo.paging_level = atoi(buf);
738 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
740 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
742 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
744 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
746 case 17: ipc->ServInfo.load_avg = atof(buf);
748 case 18: ipc->ServInfo.worker_avg = atof(buf);
750 case 19: ipc->ServInfo.thread_count = atoi(buf);
752 case 20: ipc->ServInfo.has_sieve = atoi(buf);
754 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
756 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
758 case 24: ipc->ServInfo.guest_logins = atoi(buf);
764 if (listing) free(listing);
770 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
775 if (!cret) return -2;
776 if (!listing) return -2;
777 if (*listing) return -2;
779 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
785 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
787 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
792 if (!cret) return -2;
795 sprintf(aaa, "SLRP %ld", msgnum);
798 sprintf(aaa, "SLRP HIGHEST");
800 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
806 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
811 if (!cret) return -2;
812 if (!username) return -2;
814 aaa = (char *)malloc(strlen(username) + 6);
817 sprintf(aaa, "INVT %s", username);
818 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
825 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
830 if (!cret) return -1;
831 if (!username) return -1;
833 aaa = (char *)malloc(strlen(username) + 6);
835 sprintf(aaa, "KICK %s", username);
836 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
843 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
847 if (!cret) return -2;
848 if (!qret) return -2;
849 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
850 if (!*qret) return -1;
852 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
853 if (ret / 100 == 2) {
854 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
855 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
856 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
857 qret[0]->QRflags = extract_int(cret, 3);
858 qret[0]->QRfloor = extract_int(cret, 4);
859 qret[0]->QRorder = extract_int(cret, 5);
860 qret[0]->QRdefaultview = extract_int(cret, 6);
861 qret[0]->QRflags2 = extract_int(cret, 7);
868 /* set forget to kick all users out of room */
869 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
874 if (!cret) return -2;
875 if (!qret) return -2;
877 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
878 strlen(qret->QRdirname) + 64);
881 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
882 qret->QRname, qret->QRpasswd, qret->QRdirname,
883 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
884 qret->QRdefaultview, qret->QRflags2);
885 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
892 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
894 if (!cret) return -1;
896 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
901 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
906 if (!cret) return -2;
907 if (!username) return -2;
909 aaa = (char *)malloc(strlen(username) + 6);
912 sprintf(aaa, "SETA %s", username);
913 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
920 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
926 if (!cret) return -2;
929 if (mr->references) {
930 for (ptr=mr->references; *ptr != 0; ++ptr) {
931 if (*ptr == '|') *ptr = '!';
935 snprintf(cmd, sizeof cmd,
936 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
937 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
938 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
940 if ((flag == 0) && (subject_required != NULL)) {
941 /* Is the server strongly recommending that the user enter a message subject? */
942 if ((cret[3] != '\0') && (cret[4] != '\0')) {
943 *subject_required = extract_int(&cret[4], 1);
953 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
957 if (!cret) return -2;
958 if (!iret) return -2;
959 if (*iret) return -2;
961 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
966 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
970 if (!cret) return -2;
971 if (!msgnum) return -2;
973 sprintf(aaa, "DELE %ld", msgnum);
974 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
979 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
984 if (!cret) return -2;
985 if (!destroom) return -2;
986 if (!msgnum) return -2;
988 aaa = (char *)malloc(strlen(destroom) + 28);
991 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
992 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
999 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
1003 if (!cret) return -2;
1005 sprintf(aaa, "KILL %d", for_real);
1006 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1011 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
1012 const char *password, int floor, char *cret)
1017 if (!cret) return -2;
1018 if (!roomname) return -2;
1021 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
1022 if (!aaa) return -1;
1023 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
1026 aaa = (char *)malloc(strlen(roomname) + 40);
1027 if (!aaa) return -1;
1028 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1031 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1038 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1040 if (!cret) return -2;
1042 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1047 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1053 if (!cret) return -2;
1054 if (!mret) return -2;
1055 if (*mret) return -2;
1056 if (!message) return -2;
1058 aaa = (char *)malloc(strlen(message) + 6);
1059 if (!aaa) return -1;
1061 sprintf(aaa, "MESG %s", message);
1062 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1069 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1071 if (!cret) return -2;
1073 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1078 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1084 if (!cret) return -2;
1085 if (!rret) return -2;
1086 if (*rret) return -2;
1089 aaa = (char *)malloc(strlen(username) + 6);
1091 aaa = (char *)malloc(12);
1092 if (!aaa) return -1;
1095 sprintf(aaa, "GREG %s", username);
1097 sprintf(aaa, "GREG _SELF_");
1098 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1105 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1110 if (!cret) return -2;
1111 if (!username) return -2;
1112 if (axlevel < AxDeleted || axlevel > AxAideU) return -2;
1114 aaa = (char *)malloc(strlen(username) + 17);
1115 if (!aaa) return -1;
1117 sprintf(aaa, "VALI %s|%d", username, axlevel);
1118 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1125 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1129 if (!cret) return -1;
1130 if (!info) return -1;
1132 sprintf(aaa, "EINF %d", for_real);
1133 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1138 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1144 if (!cret) return -1;
1145 if (!listing) return -1;
1146 if (*listing) return -1;
1147 if (!searchstring) return -1;
1149 cmd = malloc(strlen(searchstring) + 10);
1150 sprintf(cmd, "LIST %s", searchstring);
1152 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1159 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1161 if (!cret) return -1;
1162 if (!info) return -1;
1164 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1170 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1174 if (!cret) return -1;
1175 if (!chek) return -1;
1177 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1178 if (ret / 100 == 2) {
1179 chek->newmail = extract_long(cret, 0);
1180 chek->needregis = extract_int(cret, 1);
1181 chek->needvalid = extract_int(cret, 2);
1188 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1193 if (!cret) return -2;
1194 if (!filename) return -2;
1196 aaa = (char *)malloc(strlen(filename) + 6);
1197 if (!aaa) return -1;
1199 sprintf(aaa, "DELF %s", filename);
1200 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1207 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1212 if (!cret) return -2;
1213 if (!filename) return -2;
1214 if (!destroom) return -2;
1216 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1217 if (!aaa) return -1;
1219 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1220 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1227 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1232 if (!cret) return -1;
1233 if (!listing) return -1;
1234 if (*listing) return -1;
1236 *stamp = CtdlIPCServerTime(ipc, cret);
1238 *stamp = time(NULL);
1239 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1245 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1247 void (*progress_gauge_callback)
1248 (CtdlIPC*, unsigned long, unsigned long),
1257 if (!cret) return -2;
1258 if (!filename) return -2;
1259 if (!buf) return -2;
1260 if (*buf) return -2;
1261 if (ipc->downloading) return -2;
1263 aaa = (char *)malloc(strlen(filename) + 6);
1264 if (!aaa) return -1;
1266 sprintf(aaa, "OPEN %s", filename);
1267 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1269 if (ret / 100 == 2) {
1270 ipc->downloading = 1;
1271 bytes = extract_long(cret, 0);
1272 last_mod = extract_int(cret, 1);
1273 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1275 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1276 progress_gauge_callback, cret);
1278 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1279 progress_gauge_callback, cret);
1282 ret = CtdlIPCEndDownload(ipc, cret);
1284 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1285 filename, mimetype);
1292 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1294 void (*progress_gauge_callback)
1295 (CtdlIPC*, unsigned long, unsigned long),
1305 if (!cret) return -2;
1306 if (!buf) return -2;
1307 if (*buf) return -2;
1308 if (!part) return -2;
1309 if (!msgnum) return -2;
1310 if (ipc->downloading) return -2;
1312 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1313 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1314 if (ret / 100 == 2) {
1315 ipc->downloading = 1;
1316 bytes = extract_long(cret, 0);
1317 last_mod = extract_int(cret, 1);
1318 extract_token(filename, cret, 2, '|', sizeof filename);
1319 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1320 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1321 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1322 ret = CtdlIPCEndDownload(ipc, cret);
1324 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1325 filename, mimetype);
1332 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1333 void (*progress_gauge_callback)
1334 (CtdlIPC*, unsigned long, unsigned long),
1343 if (!cret) return -1;
1344 if (!buf) return -1;
1345 if (*buf) return -1;
1346 if (!filename) return -1;
1347 if (ipc->downloading) return -1;
1349 aaa = (char *)malloc(strlen(filename) + 6);
1350 if (!aaa) return -1;
1352 sprintf(aaa, "OIMG %s", filename);
1353 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1355 if (ret / 100 == 2) {
1356 ipc->downloading = 1;
1357 bytes = extract_long(cret, 0);
1358 last_mod = extract_int(cret, 1);
1359 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1360 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1361 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1362 ret = CtdlIPCEndDownload(ipc, cret);
1364 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1365 filename, mimetype);
1372 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1374 void (*progress_gauge_callback)
1375 (CtdlIPC*, unsigned long, unsigned long),
1381 char MimeTestBuf[64];
1382 const char *MimeType;
1385 if (!cret) return -1;
1386 if (!save_as) return -1;
1387 if (!comment) return -1;
1388 if (!path) return -1;
1389 if (!*path) return -1;
1390 if (ipc->uploading) return -1;
1392 uploadFP = fopen(path, "r");
1393 if (!uploadFP) return -2;
1395 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1400 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1401 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1402 if (!aaa) return -1;
1404 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1405 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1407 if (ret / 100 == 2) {
1409 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1410 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1418 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1419 const char *save_as,
1420 void (*progress_gauge_callback)
1421 (CtdlIPC*, unsigned long, unsigned long),
1427 char MimeTestBuf[64];
1428 const char *MimeType;
1431 if (!cret) return -1;
1432 if (!save_as) return -1;
1433 if (!path && for_real) return -1;
1434 if (!*path && for_real) return -1;
1435 if (ipc->uploading) return -1;
1437 aaa = (char *)malloc(strlen(save_as) + 17);
1438 if (!aaa) return -1;
1440 uploadFP = fopen(path, "r");
1441 if (!uploadFP) return -2;
1443 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1447 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1449 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1450 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1452 if (ret / 100 == 2 && for_real) {
1454 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1455 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1463 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1468 if (!cret) return -2;
1469 if (!username) return -2;
1471 aaa = (char *)malloc(strlen(username) + 6);
1472 if (!aaa) return -1;
1474 sprintf(aaa, "QUSR %s", username);
1475 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1482 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1486 if (!cret) return -2;
1487 if (!listing) return -2;
1488 if (*listing) return -2;
1490 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1495 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1500 if (!cret) return -2;
1501 if (!name) return -2;
1503 sprintf(aaa, "CFLR %s|%d", name, for_real);
1504 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1510 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1514 if (!cret) return -1;
1515 if (floornum < 0) return -1;
1517 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1518 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1523 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1528 if (!cret) return -2;
1529 if (!floorname) return -2;
1530 if (floornum < 0) return -2;
1532 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1533 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1541 * You only need to fill out hostname, the defaults will be used if any of the
1542 * other fields are not set properly.
1544 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1545 int revision, const char *software_name, const char *hostname,
1551 if (developerid < 0 || clientid < 0 || revision < 0 ||
1555 revision = REV_LEVEL - 600;
1556 software_name = "Citadel (libcitadel)";
1558 if (!hostname) return -2;
1560 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1561 if (!aaa) return -1;
1563 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1564 revision, software_name, hostname);
1565 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1572 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1578 if (!cret) return -2;
1579 if (!username) return -2;
1581 aaa = (char *)malloc(strlen(username) + 8);
1582 if (!aaa) return -1;
1585 sprintf(aaa, "SEXP %s|-", username);
1586 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1589 sprintf(aaa, "SEXP %s||", username);
1590 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1598 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1602 if (!cret) return -2;
1603 if (!listing) return -2;
1604 if (*listing) return -2;
1606 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1611 /* mode is 0 = enable, 1 = disable, 2 = status */
1612 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1616 if (!cret) return -2;
1618 sprintf(aaa, "DEXP %d", mode);
1619 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1624 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1626 if (!cret) return -2;
1627 if (!bio) return -2;
1629 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1635 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1641 if (!cret) return -2;
1642 if (!username) return -2;
1643 if (!listing) return -2;
1644 if (*listing) return -2;
1646 aaa = (char *)malloc(strlen(username) + 6);
1647 if (!aaa) return -1;
1649 sprintf(aaa, "RBIO %s", username);
1650 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1657 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1661 if (!cret) return -2;
1662 if (!listing) return -2;
1663 if (*listing) return -2;
1665 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1670 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1674 if (!cret) return -1;
1676 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1677 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1682 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1686 if (!cret) return -1;
1688 sprintf(aaa, "TERM %d", sid);
1689 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1694 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1696 if (!cret) return -1;
1698 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1703 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1707 if (!cret) return -1;
1709 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1710 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1715 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1721 if (!cret) return -2;
1722 if (!text) return -2;
1723 if (!filename) return -2;
1725 aaa = (char *)malloc(strlen(filename) + 6);
1726 if (!aaa) return -1;
1728 sprintf(aaa, "EMSG %s", filename);
1729 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1736 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1741 if (!cret) return -2;
1742 if (!hostname) return -2;
1744 aaa = (char *)malloc(strlen(hostname) + 6);
1745 if (!aaa) return -1;
1747 sprintf(aaa, "HCHG %s", hostname);
1748 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1755 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1760 if (!cret) return -2;
1761 if (!roomname) return -2;
1763 aaa = (char *)malloc(strlen(roomname) + 6);
1764 if (!aaa) return -1;
1766 sprintf(aaa, "RCHG %s", roomname);
1767 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1774 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1779 if (!cret) return -2;
1780 if (!username) return -2;
1782 aaa = (char *)malloc(strlen(username) + 6);
1783 if (!aaa) return -1;
1785 sprintf(aaa, "UCHG %s", username);
1786 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1793 /* This function returns the actual server time reported, or 0 if error */
1794 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1796 register time_t tret;
1799 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1800 if (ret / 100 == 2) {
1801 tret = extract_long(cret, 0);
1810 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1811 struct ctdluser **uret, char *cret)
1816 if (!cret) return -2;
1817 if (!uret) return -2;
1818 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1819 if (!*uret) return -1;
1821 sprintf(aaa, "AGUP %s", who);
1822 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1824 if (ret / 100 == 2) {
1825 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1826 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1827 uret[0]->flags = extract_int(cret, 2);
1828 uret[0]->timescalled = extract_long(cret, 3);
1829 uret[0]->posted = extract_long(cret, 4);
1830 uret[0]->axlevel = extract_int(cret, 5);
1831 uret[0]->usernum = extract_long(cret, 6);
1832 uret[0]->lastcall = extract_long(cret, 7);
1833 uret[0]->USuserpurge = extract_int(cret, 8);
1840 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1845 if (!cret) return -2;
1846 if (!uret) return -2;
1848 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1849 if (!aaa) return -1;
1851 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1852 uret->fullname, uret->password, uret->flags,
1853 uret->timescalled, uret->posted, uret->axlevel,
1854 uret->usernum, uret->lastcall, uret->USuserpurge);
1855 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1862 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1863 /* caller must free the struct ExpirePolicy */
1864 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
1865 struct ExpirePolicy **policy, char *cret)
1867 static char *proto[] = {
1871 strof(mailboxespolicy)
1876 if (!cret) return -2;
1877 if (!policy) return -2;
1878 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1879 if (!*policy) return -1;
1880 if (which < 0 || which > 3) return -2;
1882 sprintf(cmd, "GPEX %s", proto[which]);
1883 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1884 if (ret / 100 == 2) {
1885 policy[0]->expire_mode = extract_int(cret, 0);
1886 policy[0]->expire_value = extract_int(cret, 1);
1893 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1894 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1895 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1896 struct ExpirePolicy *policy, char *cret)
1899 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1901 if (!cret) return -2;
1902 if (which < 0 || which > 3) return -2;
1903 if (!policy) return -2;
1904 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1905 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1907 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1908 policy->expire_mode, policy->expire_value);
1909 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1914 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1918 if (!cret) return -2;
1919 if (!listing) return -2;
1920 if (*listing) return -2;
1922 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1923 listing, &bytes, cret);
1928 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1930 if (!cret) return -2;
1931 if (!listing) return -2;
1933 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1939 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1940 char **listing, char *cret)
1946 if (!cret) return -2;
1947 if (!mimetype) return -2;
1948 if (!listing) return -2;
1949 if (*listing) return -2;
1951 aaa = malloc(strlen(mimetype) + 13);
1952 if (!aaa) return -1;
1953 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1954 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1955 listing, &bytes, cret);
1962 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1963 const char *listing, char *cret)
1968 if (!cret) return -2;
1969 if (!mimetype) return -2;
1970 if (!listing) return -2;
1972 aaa = malloc(strlen(mimetype) + 13);
1973 if (!aaa) return -1;
1974 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1975 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1983 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1987 if (!cret) return -2;
1988 if (!listing) return -2;
1989 if (*listing) return -2;
1991 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1992 listing, &bytes, cret);
1997 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1999 if (!cret) return -2;
2000 if (!listing) return -2;
2002 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
2008 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2012 if (!cret) return -2;
2013 if (session < 0) return -2;
2015 sprintf(aaa, "REQT %d", session);
2016 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2021 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2025 if (!cret) return -2;
2026 if (msgnum < 0) return -2;
2028 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2029 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2034 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2043 /* New SSL object */
2044 temp_ssl = SSL_new(ssl_ctx);
2046 error_printf("SSL_new failed: %s\n",
2047 ERR_reason_error_string(ERR_get_error()));
2050 /* Pointless flag waving */
2051 #if SSLEAY_VERSION_NUMBER >= 0x0922
2052 SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
2055 if (!access(EGD_POOL, F_OK))
2058 if (!RAND_status()) {
2059 error_printf("PRNG not properly seeded\n");
2063 /* Associate network connection with SSL object */
2064 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2065 error_printf("SSL_set_fd failed: %s\n",
2066 ERR_reason_error_string(ERR_get_error()));
2070 if (status_hook != NULL)
2071 status_hook("Requesting encryption...\r");
2073 /* Ready to start SSL/TLS */
2075 CtdlIPC_putline(ipc, "STLS");
2076 CtdlIPC_getline(ipc, buf);
2077 if (buf[0] != '2') {
2078 error_printf("Server can't start TLS: %s\n", buf);
2082 r = CtdlIPCGenericCommand(ipc,
2083 "STLS", NULL, 0, NULL, NULL, cret);
2085 error_printf("Server can't start TLS: %s\n", buf);
2090 /* Do SSL/TLS handshake */
2091 if ((a = SSL_connect(temp_ssl)) < 1) {
2092 error_printf("SSL_connect failed: %s\n",
2093 ERR_reason_error_string(ERR_get_error()));
2097 ipc->ssl = temp_ssl;
2099 if (BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE))
2103 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2104 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2105 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2106 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2112 #endif /* HAVE_OPENSSL */
2117 static void endtls(SSL *ssl)
2128 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2133 if (!address) return -2;
2134 if (!cret) return -2;
2136 aaa = (char *)malloc(strlen(address) + 6);
2137 if (!aaa) return -1;
2139 sprintf(aaa, "QDIR %s", address);
2140 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2147 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2151 if (!cret) return -2;
2152 sprintf(aaa, "IPGM %d", secret);
2153 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2158 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2162 if (!cret) return -2;
2163 if (!mret) return -2;
2164 if (*mret) return -2;
2166 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2187 /* ************************************************************************** */
2188 /* Stuff below this line is not for public consumption */
2189 /* ************************************************************************** */
2192 /* Read a listing from the server up to 000. Append to dest if it exists */
2193 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2202 length = strlen(ret);
2207 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2208 linelength = strlen(aaa);
2209 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2211 strcpy(&ret[length], aaa);
2212 length += linelength;
2213 strcpy(&ret[length++], "\n");
2221 /* Send a listing to the server; generate the ending 000. */
2222 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2226 text = (char *)malloc(strlen(listing) + 6);
2228 strcpy(text, listing);
2229 while (text[strlen(text) - 1] == '\n')
2230 text[strlen(text) - 1] = '\0';
2231 strcat(text, "\n000");
2232 CtdlIPC_putline(ipc, text);
2236 /* Malloc failed but we are committed to send */
2237 /* This may result in extra blanks at the bottom */
2238 CtdlIPC_putline(ipc, text);
2239 CtdlIPC_putline(ipc, "000");
2245 /* Partial read of file from server */
2246 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2248 register size_t len = 0;
2252 if (!cret) return 0;
2253 if (bytes < 1) return 0;
2256 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2257 CtdlIPC_putline(ipc, aaa);
2258 CtdlIPC_getline(ipc, aaa);
2260 strcpy(cret, &aaa[4]);
2262 len = extract_long(&aaa[4], 0);
2263 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2265 /* I know what I'm doing */
2266 serv_read(ipc, ((char *)(*buf) + offset), len);
2268 /* We have to read regardless */
2269 serv_read(ipc, aaa, len);
2273 CtdlIPC_unlock(ipc);
2279 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2283 if (!cret) return -2;
2284 if (!ipc->downloading) return -2;
2286 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2288 ipc->downloading = 0;
2294 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2298 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2299 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2306 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2307 void (*progress_gauge_callback)
2308 (CtdlIPC*, unsigned long, unsigned long),
2311 register size_t len;
2313 if (!cret) return -1;
2314 if (!buf) return -1;
2315 if (*buf) return -1;
2316 if (!ipc->downloading) return -1;
2319 if (progress_gauge_callback)
2320 progress_gauge_callback(ipc, len, bytes);
2321 while (len < bytes) {
2322 register size_t block;
2324 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2330 if (progress_gauge_callback)
2331 progress_gauge_callback(ipc, len, bytes);
2336 /* READ - pipelined */
2337 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2339 void (*progress_gauge_callback)
2340 (CtdlIPC*, unsigned long, unsigned long),
2343 register size_t len;
2344 register int calls; /* How many calls in the pipeline */
2345 register int i; /* iterator */
2348 if (!cret) return -1;
2349 if (!buf) return -1;
2350 if (*buf) return -1;
2351 if (!ipc->downloading) return -1;
2353 *buf = (void *)realloc(*buf, bytes - resume);
2354 if (!*buf) return -1;
2358 if (progress_gauge_callback)
2359 progress_gauge_callback(ipc, len, bytes);
2361 /* How many calls will be in the pipeline? */
2362 calls = (bytes - resume) / 4096;
2363 if ((bytes - resume) % 4096) calls++;
2365 /* Send all requests at once */
2366 for (i = 0; i < calls; i++) {
2367 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2368 CtdlIPC_putline(ipc, aaa);
2371 /* Receive all responses at once */
2372 for (i = 0; i < calls; i++) {
2373 CtdlIPC_getline(ipc, aaa);
2375 strcpy(cret, &aaa[4]);
2377 len = extract_long(&aaa[4], 0);
2378 /* I know what I'm doing */
2379 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2381 if (progress_gauge_callback)
2382 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2384 CtdlIPC_unlock(ipc);
2390 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2395 if (!cret) return -1;
2396 if (!ipc->uploading) return -1;
2398 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2399 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2406 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2407 void (*progress_gauge_callback)
2408 (CtdlIPC*, unsigned long, unsigned long),
2411 register int ret = -1;
2412 register size_t offset = 0;
2416 FILE *fd = uploadFP;
2419 if (!cret) return -1;
2421 fseek(fd, 0L, SEEK_END);
2425 if (progress_gauge_callback)
2426 progress_gauge_callback(ipc, 0, bytes);
2428 while (offset < bytes) {
2429 register size_t to_write;
2431 /* Read some data in */
2432 to_write = fread(buf, 1, 4096, fd);
2434 if (feof(fd) || ferror(fd)) break;
2436 sprintf(aaa, "WRIT %d", (int)to_write);
2437 CtdlIPC_putline(ipc, aaa);
2438 CtdlIPC_getline(ipc, aaa);
2439 strcpy(cret, &aaa[4]);
2441 if (aaa[0] == '7') {
2442 to_write = extract_long(&aaa[4], 0);
2444 serv_write(ipc, buf, to_write);
2446 if (progress_gauge_callback)
2447 progress_gauge_callback(ipc, offset, bytes);
2448 /* Detect short reads and back up if needed */
2449 /* offset will never be negative anyway */
2450 fseek(fd, (signed)offset, SEEK_SET);
2455 if (progress_gauge_callback)
2456 progress_gauge_callback(ipc, 1, 1);
2459 return (!ferr ? ret : -2);
2464 * Generic command method. This method should handle any server command
2465 * except for CHAT. It takes the following arguments:
2467 * ipc The server to speak with
2468 * command Preformatted command to send to server
2469 * to_send A text or binary file to send to server
2470 * (only sent if server requests it)
2471 * bytes_to_send The number of bytes in to_send (required if
2472 * sending binary, optional if sending listing)
2473 * to_receive Pointer to a NULL pointer, if the server
2474 * sends text or binary we will allocate memory
2475 * for the file and stuff it here
2476 * bytes_to_receive If a file is received, we will store its
2478 * proto_response The protocol response. Caller must provide
2479 * this buffer and ensure that it is at least
2480 * 128 bytes in length.
2482 * This function returns a number equal to the protocol response number,
2483 * -1 if an internal error occurred, -2 if caller provided bad values,
2484 * or 0 - the protocol response number if bad values were found during
2485 * the protocol exchange.
2486 * It stores the protocol response string (minus the number) in
2487 * protocol_response as described above. Some commands send additional
2488 * data in this string.
2490 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2491 const char *command, const char *to_send,
2492 size_t bytes_to_send, char **to_receive,
2493 size_t *bytes_to_receive, char *proto_response)
2499 if (!command) return -2;
2500 if (!proto_response) return -2;
2503 if (ipc->ssl) watch_ssl = 1;
2507 CtdlIPC_putline(ipc, command);
2509 CtdlIPC_getline(ipc, proto_response);
2510 if (proto_response[3] == '*')
2512 ret = atoi(proto_response);
2513 strcpy(proto_response, &proto_response[4]);
2514 switch (ret / 100) {
2515 default: /* Unknown, punt */
2517 case 3: /* MORE_DATA */
2519 /* Don't need to do anything */
2521 case 1: /* LISTING_FOLLOWS */
2522 if (to_receive && !*to_receive && bytes_to_receive) {
2523 *to_receive = CtdlIPCReadListing(ipc, NULL);
2524 } else { /* Drain */
2525 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2529 case 4: /* SEND_LISTING */
2531 CtdlIPCSendListing(ipc, to_send);
2533 /* No listing given, fake it */
2534 CtdlIPC_putline(ipc, "000");
2538 case 6: /* BINARY_FOLLOWS */
2539 if (to_receive && !*to_receive && bytes_to_receive) {
2541 extract_long(proto_response, 0);
2542 *to_receive = (char *)
2543 malloc((size_t)*bytes_to_receive);
2547 serv_read(ipc, *to_receive,
2554 drain = extract_long(proto_response, 0);
2555 while (drain > SIZ) {
2556 serv_read(ipc, buf, SIZ);
2559 serv_read(ipc, buf, drain);
2563 case 7: /* SEND_BINARY */
2564 if (to_send && bytes_to_send) {
2565 serv_write(ipc, to_send, bytes_to_send);
2566 } else if (bytes_to_send) {
2567 /* Fake it, send nulls */
2570 fake = bytes_to_send;
2571 memset(buf, '\0', SIZ);
2572 while (fake > SIZ) {
2573 serv_write(ipc, buf, SIZ);
2576 serv_write(ipc, buf, fake);
2578 } /* else who knows? DANGER WILL ROBINSON */
2580 case 8: /* START_CHAT_MODE */
2581 if (!strncasecmp(command, "CHAT", 4)) {
2582 /* Don't call chatmode with generic! */
2583 CtdlIPC_putline(ipc, "/quit");
2586 /* In this mode we send then receive listing */
2588 CtdlIPCSendListing(ipc, to_send);
2590 /* No listing given, fake it */
2591 CtdlIPC_putline(ipc, "000");
2594 if (to_receive && !*to_receive
2595 && bytes_to_receive) {
2596 *to_receive = CtdlIPCReadListing(ipc, NULL);
2597 } else { /* Drain */
2598 while (CtdlIPC_getline(ipc, buf),
2599 strcmp(buf, "000")) ;
2604 case 9: /* ASYNC_MSG */
2605 /* CtdlIPCDoAsync(ret, proto_response); */
2606 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2612 CtdlIPC_unlock(ipc);
2618 * Connect to a Citadel on a remote host using a TCP/IP socket
2620 static int tcp_connectsock(char *host, char *service)
2622 struct in6_addr serveraddr;
2623 struct addrinfo hints;
2624 struct addrinfo *res = NULL;
2625 struct addrinfo *ai = NULL;
2629 if ((host == NULL) || IsEmptyStr(host)) {
2630 service = DEFAULT_HOST ;
2632 if ((service == NULL) || IsEmptyStr(service)) {
2633 service = DEFAULT_PORT ;
2636 memset(&hints, 0x00, sizeof(hints));
2637 hints.ai_flags = AI_NUMERICSERV;
2638 hints.ai_family = AF_UNSPEC;
2639 hints.ai_socktype = SOCK_STREAM;
2642 * Handle numeric IPv4 and IPv6 addresses
2644 rc = inet_pton(AF_INET, host, &serveraddr);
2645 if (rc == 1) { /* dotted quad */
2646 hints.ai_family = AF_INET;
2647 hints.ai_flags |= AI_NUMERICHOST;
2650 rc = inet_pton(AF_INET6, host, &serveraddr);
2651 if (rc == 1) { /* IPv6 address */
2652 hints.ai_family = AF_INET6;
2653 hints.ai_flags |= AI_NUMERICHOST;
2657 /* Begin the connection process */
2659 rc = getaddrinfo(host, service, &hints, &res);
2665 * Try all available addresses until we connect to one or until we run out.
2667 for (ai = res; ai != NULL; ai = ai->ai_next) {
2668 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2669 if (sock < 0) return(-1);
2671 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2673 return(sock); /* Connected! */
2676 close(sock); /* Failed. Close the socket to avoid fd leak! */
2688 * Connect to a Citadel on the local host using a unix domain socket
2690 static int uds_connectsock(int *isLocal, char *sockpath)
2692 struct sockaddr_un addr;
2695 memset(&addr, 0, sizeof(addr));
2696 addr.sun_family = AF_UNIX;
2697 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2699 s = socket(AF_UNIX, SOCK_STREAM, 0);
2704 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2715 * input binary data from socket
2717 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2719 unsigned int len, rlen;
2721 #if defined(HAVE_OPENSSL)
2723 serv_read_ssl(ipc, buf, bytes);
2728 while (len < bytes) {
2729 rlen = read(ipc->sock, &buf[len], bytes - len);
2731 connection_died(ipc, 0);
2740 * send binary to server
2742 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2744 unsigned int bytes_written = 0;
2747 #if defined(HAVE_OPENSSL)
2749 serv_write_ssl(ipc, buf, nbytes);
2753 while (bytes_written < nbytes) {
2754 retval = write(ipc->sock, &buf[bytes_written],
2755 nbytes - bytes_written);
2757 connection_died(ipc, 0);
2760 bytes_written += retval;
2767 * input binary data from encrypted connection
2769 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2775 while (len < bytes) {
2776 if (SSL_want_read(ipc->ssl)) {
2777 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2778 error_printf("SSL_write in serv_read:\n");
2779 ERR_print_errors_fp(stderr);
2782 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2786 errval = SSL_get_error(ipc->ssl, rlen);
2787 if (errval == SSL_ERROR_WANT_READ ||
2788 errval == SSL_ERROR_WANT_WRITE) {
2793 Not sure why we'd want to handle these error codes any differently,
2794 but this definitely isn't the way to handle them. Someone must have
2795 naively assumed that we could fall back to unencrypted communications,
2796 but all it does is just recursively blow the stack.
2797 if (errval == SSL_ERROR_ZERO_RETURN ||
2798 errval == SSL_ERROR_SSL) {
2799 serv_read(ipc, &buf[len], bytes - len);
2803 error_printf("SSL_read in serv_read: %s\n",
2804 ERR_reason_error_string(ERR_peek_error()));
2805 connection_died(ipc, 1);
2814 * send binary to server encrypted
2816 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2818 unsigned int bytes_written = 0;
2822 while (bytes_written < nbytes) {
2823 if (SSL_want_write(ipc->ssl)) {
2824 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2825 error_printf("SSL_read in serv_write:\n");
2826 ERR_print_errors_fp(stderr);
2829 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2830 nbytes - bytes_written);
2834 errval = SSL_get_error(ipc->ssl, retval);
2835 if (errval == SSL_ERROR_WANT_READ ||
2836 errval == SSL_ERROR_WANT_WRITE) {
2840 if (errval == SSL_ERROR_ZERO_RETURN ||
2841 errval == SSL_ERROR_SSL) {
2842 serv_write(ipc, &buf[bytes_written],
2843 nbytes - bytes_written);
2846 error_printf("SSL_write in serv_write: %s\n",
2847 ERR_reason_error_string(ERR_peek_error()));
2848 connection_died(ipc, 1);
2851 bytes_written += retval;
2856 #ifdef THREADED_CLIENT
2857 static void ssl_lock(int mode, int n, const char *file, int line)
2859 if (mode & CRYPTO_LOCK)
2860 pthread_mutex_lock(Critters[n]);
2862 pthread_mutex_unlock(Critters[n]);
2864 #endif /* THREADED_CLIENT */
2867 static void CtdlIPC_init_OpenSSL(void)
2870 SSL_METHOD *ssl_method;
2873 /* already done init */
2882 SSL_load_error_strings();
2883 SSLeay_add_ssl_algorithms();
2885 /* Set up the SSL context in which we will oeprate */
2886 ssl_method = SSLv23_client_method();
2887 ssl_ctx = SSL_CTX_new(ssl_method);
2889 error_printf("SSL_CTX_new failed: %s\n",
2890 ERR_reason_error_string(ERR_get_error()));
2893 /* Any reasonable cipher we can get */
2894 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2895 error_printf("No ciphers available for encryption\n");
2898 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2900 /* Load DH parameters into the context */
2903 error_printf("Can't allocate a DH object: %s\n",
2904 ERR_reason_error_string(ERR_get_error()));
2907 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2908 error_printf("Can't assign DH_P: %s\n",
2909 ERR_reason_error_string(ERR_get_error()));
2913 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2914 error_printf("Can't assign DH_G: %s\n",
2915 ERR_reason_error_string(ERR_get_error()));
2920 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2923 #ifdef THREADED_CLIENT
2924 /* OpenSSL requires callbacks for threaded clients */
2925 CRYPTO_set_locking_callback(ssl_lock);
2926 CRYPTO_set_id_callback(id_callback);
2928 /* OpenSSL requires us to do semaphores for threaded clients */
2929 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2931 perror("malloc failed");
2934 for (a = 0; a < CRYPTO_num_locks(); a++) {
2935 Critters[a] = malloc(sizeof (pthread_mutex_t));
2937 perror("malloc failed");
2940 pthread_mutex_init(Critters[a], NULL);
2943 #endif /* THREADED_CLIENT */
2948 #ifdef THREADED_CLIENT
2949 static unsigned long id_callback(void) {
2950 return (unsigned long)pthread_self();
2952 #endif /* THREADED_CLIENT */
2953 #endif /* HAVE_OPENSSL */
2957 ReadNetworkChunk(CtdlIPC* ipc)
2974 FD_SET(ipc->sock, &read_fd);
2975 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2977 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2981 *(ipc->BufPtr) = '\0';
2982 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2983 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2985 ipc->BufPtr[n]='\0';
2993 if (!(errno == EINTR || errno == EAGAIN))
2994 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
3000 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3002 ipc->BufPtr[n]='\0';
3007 connection_died(ipc, 0);
3015 * input string from socket - implemented in terms of serv_read()
3019 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3022 char *aptr, *bptr, *aeptr, *beptr;
3024 // error_printf("---\n");
3027 #if defined(HAVE_OPENSSL)
3030 /* Read one character at a time. */
3032 serv_read(ipc, &buf[i], 1);
3033 if (buf[i] == '\n' || i == (SIZ-1))
3037 /* If we got a long line, discard characters until the newline. */
3039 while (buf[i] != '\n')
3040 serv_read(ipc, &buf[i], 1);
3042 /* Strip the trailing newline (and carriage return, if present) */
3043 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3044 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3049 if (ipc->Buf == NULL)
3052 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3054 ipc->BufPtr = ipc->Buf;
3058 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3059 if (ipc->BufUsed == 0)
3060 ReadNetworkChunk(ipc);
3062 //// if (ipc->BufUsed != 0) while (1)
3068 aeptr = ipc->Buf + ipc->BufSize;
3069 while ((aptr < aeptr) &&
3073 *(bptr++) = *(aptr++);
3074 if ((*aptr == '\n') && (aptr < aeptr))
3076 /* Terminate it right, remove the line breaks */
3077 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3079 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3082 // 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);
3083 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3086 /* is there more in the buffer we need to read later? */
3087 if (ipc->Buf + ipc->BufUsed > aptr)
3094 ipc->BufPtr = ipc->Buf;
3096 // error_printf("----bla6\n");
3099 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3100 else if ((ipc->BufPtr != ipc->Buf) &&
3101 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3103 size_t NewBufSize = ipc->BufSize * 2;
3104 int delta = (ipc->BufPtr - ipc->Buf);
3107 /* if the line would end after our buffer, we should use a bigger buffer. */
3108 NewBuf = (char *)malloc (NewBufSize + 10);
3109 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3111 ipc->Buf = ipc->BufPtr = NewBuf;
3112 ipc->BufUsed -= delta;
3113 ipc->BufSize = NewBufSize;
3115 if (ReadNetworkChunk(ipc) <0)
3117 // error_printf("----bla\n");
3121 /// error_printf("----bl45761%s\nipc->BufUsed");
3123 // error_printf("----bla1\n");
3126 #else /* CHUNKED_READ */
3128 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3132 /* Read one character at a time. */
3134 serv_read(ipc, &buf[i], 1);
3135 if (buf[i] == '\n' || i == (SIZ-1))
3139 /* If we got a long line, discard characters until the newline. */
3141 while (buf[i] != '\n')
3142 serv_read(ipc, &buf[i], 1);
3144 /* Strip the trailing newline (and carriage return, if present) */
3145 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3146 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3150 #endif /* CHUNKED_READ */
3153 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3155 CtdlIPC_getline(ipc, buf);
3159 * send line to server - implemented in terms of serv_write()
3161 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3167 cmd = malloc(len + 2);
3169 /* This requires no extra memory */
3170 serv_write(ipc, buf, len);
3171 serv_write(ipc, "\n", 1);
3173 /* This is network-optimized */
3174 strncpy(cmd, buf, len);
3175 strcpy(cmd + len, "\n");
3176 serv_write(ipc, cmd, len + 1);
3180 ipc->last_command_sent = time(NULL);
3183 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3185 CtdlIPC_putline(ipc, buf);
3192 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3200 ipc = ialloc(CtdlIPC);
3204 #if defined(HAVE_OPENSSL)
3206 CtdlIPC_init_OpenSSL();
3208 #if defined(HAVE_PTHREAD_H)
3209 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3211 ipc->sock = -1; /* Not connected */
3212 ipc->isLocal = 0; /* Not local, of course! */
3213 ipc->downloading = 0;
3215 ipc->last_command_sent = 0L;
3216 ipc->network_status_cb = NULL;
3221 strcpy(cithost, DEFAULT_HOST); /* default host */
3222 strcpy(citport, DEFAULT_PORT); /* default port */
3224 /* Allow caller to supply our values (Windows) */
3225 if (hostbuf && strlen(hostbuf) > 0)
3226 strcpy(cithost, hostbuf);
3227 if (portbuf && strlen(portbuf) > 0)
3228 strcpy(citport, portbuf);
3230 /* Read host/port from command line if present */
3231 for (a = 0; a < argc; ++a) {
3234 } else if (a == 1) {
3235 strcpy(cithost, argv[a]);
3236 } else if (a == 2) {
3237 strcpy(citport, argv[a]);
3239 error_printf("%s: usage: ",argv[0]);
3240 error_printf("%s [host] [port] ",argv[0]);
3247 if ((!strcmp(cithost, "localhost"))
3248 || (!strcmp(cithost, "127.0.0.1"))) {
3252 /* If we're using a unix domain socket we can do a bunch of stuff */
3253 if (!strcmp(cithost, UDS)) {
3254 if (!strcasecmp(citport, DEFAULT_PORT)) {
3255 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3258 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3260 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3261 if (ipc->sock == -1) {
3265 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3266 if (portbuf != NULL) strcpy(portbuf, sockpath);
3267 strcpy(ipc->ip_hostname, "");
3268 strcpy(ipc->ip_address, "");
3272 ipc->sock = tcp_connectsock(cithost, citport);
3273 if (ipc->sock == -1) {
3279 /* Learn the actual network identity of the host to which we are connected */
3281 struct sockaddr_in6 clientaddr;
3282 unsigned int addrlen = sizeof(clientaddr);
3284 ipc->ip_hostname[0] = 0;
3285 ipc->ip_address[0] = 0;
3287 getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3288 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3289 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3291 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3292 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3295 /* stuff other things elsewhere */
3297 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3298 if (portbuf != NULL) strcpy(portbuf, citport);
3304 * Disconnect and delete the IPC class (destructor)
3306 void CtdlIPC_delete(CtdlIPC* ipc)
3310 SSL_shutdown(ipc->ssl);
3315 if (ipc->sock > -1) {
3316 shutdown(ipc->sock, 2); /* Close it up */
3319 if (ipc->Buf != NULL)
3328 * Disconnect and delete the IPC class (destructor)
3329 * Also NULLs out the pointer
3331 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3333 CtdlIPC_delete(*pipc);
3339 * return the file descriptor of the server socket so we can select() on it.
3341 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3344 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3351 * return one character
3353 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3356 char CtdlIPC_get(CtdlIPC* ipc)
3361 serv_read(ipc, buf, 1);