2 * Copyright (c) 1987-2018 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
14 #if TIME_WITH_SYS_TIME
15 # include <sys/time.h>
19 # include <sys/time.h>
26 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <netinet/in.h>
39 #ifdef THREADED_CLIENT
42 #include <libcitadel.h>
43 #include "citadel_ipc.h"
44 #ifdef THREADED_CLIENT
45 pthread_mutex_t rwlock;
49 static SSL_CTX *ssl_ctx;
52 #ifdef THREADED_CLIENT
53 pthread_mutex_t **Critters; /* Things that need locking */
54 #endif /* THREADED_CLIENT */
56 #endif /* HAVE_OPENSSL */
59 #define INADDR_NONE 0xffffffff
62 static void (*status_hook)(char *s) = NULL;
63 char ctdl_autoetc_dir[PATH_MAX]="";
64 char file_citadel_rc[PATH_MAX]="";
65 char ctdl_run_dir[PATH_MAX]="";
66 char ctdl_etc_dir[PATH_MAX]="";
67 char ctdl_home_directory[PATH_MAX] = "";
68 char file_citadel_socket[PATH_MAX]="";
90 void CtdlIPC_lock(CtdlIPC *ipc)
92 if (ipc->network_status_cb) ipc->network_status_cb(1);
93 #ifdef THREADED_CLIENT
94 pthread_mutex_lock(&(ipc->mutex));
99 void CtdlIPC_unlock(CtdlIPC *ipc)
101 #ifdef THREADED_CLIENT
102 pthread_mutex_unlock(&(ipc->mutex));
104 if (ipc->network_status_cb) ipc->network_status_cb(0);
112 char *libcitadelclient_version_string(void) {
113 return "libcitadelclient(unnumbered)";
119 #define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
120 snprintf(SUBDIR,sizeof SUBDIR, "%s%s%s%s%s%s%s", \
121 (home&!relh)?ctdl_home_directory:basedir, \
122 ((basedir!=ctdldir)&(home&!relh))?basedir:"/", \
123 ((basedir!=ctdldir)&(home&!relh))?"/":"", \
125 (relhome[0]!='\0')?"/":"",\
127 (dirbuffer[0]!='\0')?"/":"");
129 #define DBG_PRINT(A) if (dbg==1) fprintf (stderr,"%s : %s \n", #A, A)
132 void calc_dirs_n_files(int relh, int home, const char *relhome, char *ctdldir, int dbg)
134 const char* basedir = "";
135 char dirbuffer[PATH_MAX] = "";
137 StripSlashes(ctdldir, 1);
144 COMPUTE_DIRECTORY(ctdl_run_dir);
145 StripSlashes(ctdl_run_dir, 1);
148 #ifndef HAVE_AUTO_ETC_DIR
151 basedir=AUTO_ETC_DIR;
153 COMPUTE_DIRECTORY(ctdl_autoetc_dir);
154 StripSlashes(ctdl_autoetc_dir, 1);
162 COMPUTE_DIRECTORY(ctdl_etc_dir);
163 StripSlashes(ctdl_etc_dir, 1);
167 snprintf(file_citadel_rc,
168 sizeof file_citadel_rc,
171 StripSlashes(file_citadel_rc, 0);
173 snprintf(file_citadel_socket,
174 sizeof file_citadel_socket,
177 StripSlashes(file_citadel_socket, 0);
179 DBG_PRINT(ctdl_run_dir);
180 DBG_PRINT(file_citadel_socket);
181 DBG_PRINT(ctdl_etc_dir);
182 DBG_PRINT(file_citadel_rc);
185 void setCryptoStatusHook(void (*hook)(char *s)) {
189 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
190 ipc->network_status_cb = hook;
194 char instant_msgs = 0;
197 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
198 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
200 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
201 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
202 static void endtls(SSL *ssl);
203 #ifdef THREADED_CLIENT
204 static unsigned long id_callback(void);
205 #endif /* THREADED_CLIENT */
206 #endif /* HAVE_OPENSSL */
207 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
208 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
212 const char *svn_revision(void);
215 * Does nothing. The server should always return 200.
217 int CtdlIPCNoop(CtdlIPC *ipc)
221 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
226 * Does nothing interesting. The server should always return 200
227 * along with your string.
229 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
235 if (!cret) return -2;
237 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
240 sprintf(aaa, "ECHO %s", arg);
241 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
248 * Asks the server to close the connecction.
249 * Should always return 200.
251 int CtdlIPCQuit(CtdlIPC *ipc)
253 int ret = 221; /* Default to successful quit */
257 if (ipc->sock > -1) {
258 CtdlIPC_putline(ipc, "QUIT");
259 CtdlIPC_getline(ipc, aaa);
264 SSL_shutdown(ipc->ssl);
268 shutdown(ipc->sock, 2); /* Close connection; we're dead */
276 * Asks the server to log out. Should always return 200, even if no user
277 * was logged in. The user will not be logged in after this!
279 int CtdlIPCLogout(CtdlIPC *ipc)
285 CtdlIPC_putline(ipc, "LOUT");
286 CtdlIPC_getline(ipc, aaa);
294 * First stage of authentication - pass the username. Returns 300 if the
295 * username is able to log in, with the username correctly spelled in cret.
296 * Returns various 500 error codes if the user doesn't exist, etc.
298 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
303 if (!username) return -2;
304 if (!cret) return -2;
306 aaa = (char *)malloc((size_t)(strlen(username) + 6));
309 sprintf(aaa, "USER %s", username);
310 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
317 * Second stage of authentication - provide password. The server returns
318 * 200 and several arguments in cret relating to the user's account.
320 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
325 if (!passwd) return -2;
326 if (!cret) return -2;
328 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
331 sprintf(aaa, "PASS %s", passwd);
332 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
339 * Second stage of authentication - provide password. The server returns
340 * 200 and several arguments in cret relating to the user's account.
342 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
347 if (!response) return -2;
348 if (!cret) return -2;
350 aaa = (char *)malloc((size_t)(strlen(response) + 6));
353 sprintf(aaa, "PAS2 %s", response);
354 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
361 * Create a new user. This returns 200 plus the same arguments as TryPassword
362 * if selfservice is nonzero, unless there was a problem creating the account.
363 * If selfservice is zero, creates a new user but does not log out the existing
364 * user - intended for use by system administrators to create accounts on
365 * behalf of other users.
367 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
372 if (!username) return -2;
373 if (!cret) return -2;
375 aaa = (char *)malloc((size_t)(strlen(username) + 6));
378 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
379 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
386 * Changes the user's password. Returns 200 if changed, errors otherwise.
388 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
393 if (!passwd) return -2;
394 if (!cret) return -2;
396 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
399 sprintf(aaa, "SETP %s", passwd);
400 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
407 /* Caller must free the march list */
408 /* Room types are defined in enum RoomList; keep these in sync! */
409 /* floor is -1 for all, or floornum */
410 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
413 struct march *march = NULL;
414 static char *proto[] =
415 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
420 if (!listing) return -2;
421 if (*listing) return -2; /* Free the listing first */
422 if (!cret) return -2;
423 /* if (which < 0 || which > 4) return -2; */
424 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
426 sprintf(aaa, "%s %d", proto[which], floor);
427 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
428 if (ret / 100 == 1) {
431 while (bbb && strlen(bbb)) {
434 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
436 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
437 mptr = (struct march *) malloc(sizeof (struct march));
440 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
441 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
442 mptr->march_floor = (char) extract_int(aaa, 2);
443 mptr->march_order = (char) extract_int(aaa, 3);
444 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
445 mptr->march_access = (char) extract_int(aaa, 5);
452 while (mptr2->next != NULL)
466 /* Caller must free the struct ctdluser; caller may pass an existing one */
467 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
471 if (!cret) return -2;
472 if (!uret) return -2;
473 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
474 if (!*uret) return -1;
476 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
477 if (ret / 100 == 2) {
478 uret[0]->flags = extract_int(cret, 2);
485 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
489 if (!uret) return -2;
490 if (!cret) return -2;
496 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
501 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
506 if (!oldname) return -2;
507 if (!newname) return -2;
508 if (!cret) return -2;
510 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
511 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
517 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
518 struct ctdlipcroom **rret, char *cret)
523 if (!cret) return -2;
524 if (!rret) return -2;
525 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
526 if (!*rret) return -1;
529 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
534 sprintf(aaa, "GOTO %s|%s", room, passwd);
536 aaa = (char *)malloc(strlen(room) + 6);
541 sprintf(aaa, "GOTO %s", room);
543 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
544 if (ret / 100 == 2) {
545 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
546 rret[0]->RRunread = extract_long(cret, 1);
547 rret[0]->RRtotal = extract_long(cret, 2);
548 rret[0]->RRinfoupdated = extract_int(cret, 3);
549 rret[0]->RRflags = extract_int(cret, 4);
550 rret[0]->RRhighest = extract_long(cret, 5);
551 rret[0]->RRlastread = extract_long(cret, 6);
552 rret[0]->RRismailbox = extract_int(cret, 7);
553 rret[0]->RRaide = extract_int(cret, 8);
554 rret[0]->RRnewmail = extract_long(cret, 9);
555 rret[0]->RRfloor = extract_int(cret, 10);
556 rret[0]->RRcurrentview = extract_int(cret, 11);
557 rret[0]->RRdefaultview = extract_int(cret, 12);
558 /* position 13 is a trash folder flag ... irrelevant in this client */
559 rret[0]->RRflags2 = extract_int(cret, 14);
570 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
571 /* whicharg is number of messages, applies to last, first, gt, lt */
572 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
573 const char *mtemplate, unsigned long **mret, char *cret)
576 unsigned long count = 0;
577 static char *proto[] =
578 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
583 if (!cret) return -2;
584 if (!mret) return -2;
585 if (*mret) return -2;
586 if (which < 0 || which > 6) return -2;
589 sprintf(aaa, "MSGS %s||%d", proto[which],
590 (mtemplate) ? 1 : 0);
592 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
593 (mtemplate) ? 1 : 0);
594 if (mtemplate) count = strlen(mtemplate);
595 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
599 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
602 while (bbb && strlen(bbb)) {
603 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
604 remove_token(bbb, 0, '\n');
605 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
606 sizeof (unsigned long)));
608 (*mret)[count++] = atol(aaa);
620 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
621 struct ctdlipcmessage **mret, char *cret)
627 int multipart_hunting = 0;
628 char multipart_prefix[128];
631 if (!cret) return -1;
632 if (!mret) return -1;
633 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
634 if (!*mret) return -1;
635 if (!msgnum) return -1;
637 strcpy(encoding, "");
638 strcpy(mret[0]->content_type, "");
639 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
640 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
641 if (ret / 100 == 1) {
643 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
644 while (strlen(bbb) > 4 && bbb[4] == '=') {
645 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
646 remove_token(bbb, 0, '\n');
648 if (!strncasecmp(aaa, "nhdr=yes", 8))
650 else if (!strncasecmp(aaa, "from=", 5))
651 safestrncpy(mret[0]->author, &aaa[5], SIZ);
652 else if (!strncasecmp(aaa, "type=", 5))
653 mret[0]->type = atoi(&aaa[5]);
654 else if (!strncasecmp(aaa, "msgn=", 5))
655 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
656 else if (!strncasecmp(aaa, "subj=", 5))
657 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
658 else if (!strncasecmp(aaa, "rfca=", 5))
659 safestrncpy(mret[0]->email, &aaa[5], SIZ);
660 else if (!strncasecmp(aaa, "room=", 5))
661 safestrncpy(mret[0]->room, &aaa[5], SIZ);
662 else if (!strncasecmp(aaa, "rcpt=", 5))
663 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
664 else if (!strncasecmp(aaa, "wefw=", 5))
665 safestrncpy(mret[0]->references, &aaa[5], SIZ);
666 else if (!strncasecmp(aaa, "time=", 5))
667 mret[0]->time = atol(&aaa[5]);
669 /* Multipart/alternative prefix & suffix strings help
670 * us to determine which part we want to download.
672 else if (!strncasecmp(aaa, "pref=", 5)) {
673 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
674 if (!strcasecmp(multipart_prefix,
675 "multipart/alternative")) {
679 else if (!strncasecmp(aaa, "suff=", 5)) {
680 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
681 if (!strcasecmp(multipart_prefix,
682 "multipart/alternative")) {
687 else if (!strncasecmp(aaa, "part=", 5)) {
688 struct parts *ptr, *chain;
690 ptr = (struct parts *)calloc(1, sizeof (struct parts));
693 /* Fill the buffers for the caller */
694 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
695 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
696 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
697 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
698 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
699 ptr->length = extract_long(&aaa[5], 5);
700 if (!mret[0]->attachments)
701 mret[0]->attachments = ptr;
703 chain = mret[0]->attachments;
709 /* Now handle multipart/alternative */
710 if (multipart_hunting > 0) {
711 if ( (!strcasecmp(ptr->mimetype,
713 || (!strcasecmp(ptr->mimetype,
715 strcpy(mret[0]->mime_chosen,
723 /* Eliminate "text\n" */
724 remove_token(bbb, 0, '\n');
726 /* If doing a MIME thing, pull out the extra headers */
729 if (!strncasecmp(bbb, "Content-type:", 13)) {
730 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
731 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
732 striplt(mret[0]->content_type);
734 /* strip out ";charset=" portion. FIXME do something with
735 * the charset (like... convert it) instead of just throwing
738 if (strstr(mret[0]->content_type, ";") != NULL) {
739 strcpy(strstr(mret[0]->content_type, ";"), "");
743 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
744 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
745 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
746 striplt(mret[0]->mime_chosen);
748 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
749 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
750 strcpy(encoding, &encoding[26]);
753 remove_token(bbb, 0, '\n');
754 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
755 remove_token(bbb, 0, '\n');
762 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
764 int bytes_decoded = 0;
765 ccc = malloc(strlen(bbb) + 32768);
766 if (!strcasecmp(encoding, "base64")) {
767 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
769 else if (!strcasecmp(encoding, "quoted-printable")) {
770 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
772 ccc[bytes_decoded] = 0;
777 /* FIXME: Strip trailing whitespace */
778 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
781 bbb = (char *)realloc(bbb, 1);
791 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
796 if (!cret) return -2;
797 if (!listing) return -2;
798 if (*listing) return -2;
800 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
806 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
810 char *listing = NULL;
813 if (!cret) return -2;
815 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
816 if (ret / 100 == 1) {
819 while (*listing && strlen(listing)) {
820 extract_token(buf, listing, 0, '\n', sizeof buf);
821 remove_token(listing, 0, '\n');
823 case 0: ipc->ServInfo.pid = atoi(buf);
825 case 1: strcpy(ipc->ServInfo.nodename,buf);
827 case 2: strcpy(ipc->ServInfo.humannode,buf);
829 case 3: strcpy(ipc->ServInfo.fqdn,buf);
831 case 4: strcpy(ipc->ServInfo.software,buf);
833 case 5: ipc->ServInfo.rev_level = atoi(buf);
835 case 6: strcpy(ipc->ServInfo.site_location,buf);
837 case 7: strcpy(ipc->ServInfo.sysadm,buf);
839 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
841 case 10: ipc->ServInfo.ok_floors = atoi(buf);
843 case 11: ipc->ServInfo.paging_level = atoi(buf);
845 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
847 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
849 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
851 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
853 case 17: ipc->ServInfo.load_avg = atof(buf);
855 case 18: ipc->ServInfo.worker_avg = atof(buf);
857 case 19: ipc->ServInfo.thread_count = atoi(buf);
859 case 20: ipc->ServInfo.has_sieve = atoi(buf);
861 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
863 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
865 case 24: ipc->ServInfo.guest_logins = atoi(buf);
871 if (listing) free(listing);
877 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
882 if (!cret) return -2;
883 if (!listing) return -2;
884 if (*listing) return -2;
886 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
892 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
894 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
899 if (!cret) return -2;
902 sprintf(aaa, "SLRP %ld", msgnum);
905 sprintf(aaa, "SLRP HIGHEST");
907 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
913 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
918 if (!cret) return -2;
919 if (!username) return -2;
921 aaa = (char *)malloc(strlen(username) + 6);
924 sprintf(aaa, "INVT %s", username);
925 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
932 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
937 if (!cret) return -1;
938 if (!username) return -1;
940 aaa = (char *)malloc(strlen(username) + 6);
942 sprintf(aaa, "KICK %s", username);
943 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
950 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
954 if (!cret) return -2;
955 if (!qret) return -2;
956 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
957 if (!*qret) return -1;
959 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
960 if (ret / 100 == 2) {
961 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
962 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
963 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
964 qret[0]->QRflags = extract_int(cret, 3);
965 qret[0]->QRfloor = extract_int(cret, 4);
966 qret[0]->QRorder = extract_int(cret, 5);
967 qret[0]->QRdefaultview = extract_int(cret, 6);
968 qret[0]->QRflags2 = extract_int(cret, 7);
975 /* set forget to kick all users out of room */
976 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
981 if (!cret) return -2;
982 if (!qret) return -2;
984 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
985 strlen(qret->QRdirname) + 64);
988 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
989 qret->QRname, qret->QRpasswd, qret->QRdirname,
990 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
991 qret->QRdefaultview, qret->QRflags2);
992 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
999 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
1001 if (!cret) return -1;
1003 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
1008 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
1013 if (!cret) return -2;
1014 if (!username) return -2;
1016 aaa = (char *)malloc(strlen(username) + 6);
1017 if (!aaa) return -1;
1019 sprintf(aaa, "SETA %s", username);
1020 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1027 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
1033 if (!cret) return -2;
1036 if (mr->references) {
1037 for (ptr=mr->references; *ptr != 0; ++ptr) {
1038 if (*ptr == '|') *ptr = '!';
1042 snprintf(cmd, sizeof cmd,
1043 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
1044 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
1045 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
1047 if ((flag == 0) && (subject_required != NULL)) {
1048 /* Is the server strongly recommending that the user enter a message subject? */
1049 if ((cret[3] != '\0') && (cret[4] != '\0')) {
1050 *subject_required = extract_int(&cret[4], 1);
1060 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
1064 if (!cret) return -2;
1065 if (!iret) return -2;
1066 if (*iret) return -2;
1068 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
1073 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
1077 if (!cret) return -2;
1078 if (!msgnum) return -2;
1080 sprintf(aaa, "DELE %ld", msgnum);
1081 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1086 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
1091 if (!cret) return -2;
1092 if (!destroom) return -2;
1093 if (!msgnum) return -2;
1095 aaa = (char *)malloc(strlen(destroom) + 28);
1096 if (!aaa) return -1;
1098 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
1099 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1106 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
1110 if (!cret) return -2;
1112 sprintf(aaa, "KILL %d", for_real);
1113 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1118 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
1119 const char *password, int floor, char *cret)
1124 if (!cret) return -2;
1125 if (!roomname) return -2;
1128 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
1129 if (!aaa) return -1;
1130 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
1133 aaa = (char *)malloc(strlen(roomname) + 40);
1134 if (!aaa) return -1;
1135 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1138 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1145 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1147 if (!cret) return -2;
1149 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1154 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1160 if (!cret) return -2;
1161 if (!mret) return -2;
1162 if (*mret) return -2;
1163 if (!message) return -2;
1165 aaa = (char *)malloc(strlen(message) + 6);
1166 if (!aaa) return -1;
1168 sprintf(aaa, "MESG %s", message);
1169 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1176 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1178 if (!cret) return -2;
1180 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1185 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1191 if (!cret) return -2;
1192 if (!rret) return -2;
1193 if (*rret) return -2;
1196 aaa = (char *)malloc(strlen(username) + 6);
1198 aaa = (char *)malloc(12);
1199 if (!aaa) return -1;
1202 sprintf(aaa, "GREG %s", username);
1204 sprintf(aaa, "GREG _SELF_");
1205 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1212 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1217 if (!cret) return -2;
1218 if (!username) return -2;
1219 if (axlevel < AxDeleted || axlevel > AxAideU) return -2;
1221 aaa = (char *)malloc(strlen(username) + 17);
1222 if (!aaa) return -1;
1224 sprintf(aaa, "VALI %s|%d", username, axlevel);
1225 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1232 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1236 if (!cret) return -1;
1237 if (!info) return -1;
1239 sprintf(aaa, "EINF %d", for_real);
1240 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1245 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1251 if (!cret) return -1;
1252 if (!listing) return -1;
1253 if (*listing) return -1;
1254 if (!searchstring) return -1;
1256 cmd = malloc(strlen(searchstring) + 10);
1257 sprintf(cmd, "LIST %s", searchstring);
1259 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1266 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1268 if (!cret) return -1;
1269 if (!info) return -1;
1271 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1277 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1281 if (!cret) return -1;
1282 if (!chek) return -1;
1284 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1285 if (ret / 100 == 2) {
1286 chek->newmail = extract_long(cret, 0);
1287 chek->needregis = extract_int(cret, 1);
1288 chek->needvalid = extract_int(cret, 2);
1295 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1300 if (!cret) return -2;
1301 if (!filename) return -2;
1303 aaa = (char *)malloc(strlen(filename) + 6);
1304 if (!aaa) return -1;
1306 sprintf(aaa, "DELF %s", filename);
1307 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1314 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1319 if (!cret) return -2;
1320 if (!filename) return -2;
1321 if (!destroom) return -2;
1323 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1324 if (!aaa) return -1;
1326 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1327 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1334 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1339 if (!cret) return -1;
1340 if (!listing) return -1;
1341 if (*listing) return -1;
1343 *stamp = CtdlIPCServerTime(ipc, cret);
1345 *stamp = time(NULL);
1346 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1352 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1354 void (*progress_gauge_callback)
1355 (CtdlIPC*, unsigned long, unsigned long),
1364 if (!cret) return -2;
1365 if (!filename) return -2;
1366 if (!buf) return -2;
1367 if (*buf) return -2;
1368 if (ipc->downloading) return -2;
1370 aaa = (char *)malloc(strlen(filename) + 6);
1371 if (!aaa) return -1;
1373 sprintf(aaa, "OPEN %s", filename);
1374 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1376 if (ret / 100 == 2) {
1377 ipc->downloading = 1;
1378 bytes = extract_long(cret, 0);
1379 last_mod = extract_int(cret, 1);
1380 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1382 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1383 progress_gauge_callback, cret);
1385 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1386 progress_gauge_callback, cret);
1389 ret = CtdlIPCEndDownload(ipc, cret);
1391 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1392 filename, mimetype);
1399 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1401 void (*progress_gauge_callback)
1402 (CtdlIPC*, unsigned long, unsigned long),
1412 if (!cret) return -2;
1413 if (!buf) return -2;
1414 if (*buf) return -2;
1415 if (!part) return -2;
1416 if (!msgnum) return -2;
1417 if (ipc->downloading) return -2;
1419 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1420 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1421 if (ret / 100 == 2) {
1422 ipc->downloading = 1;
1423 bytes = extract_long(cret, 0);
1424 last_mod = extract_int(cret, 1);
1425 extract_token(filename, cret, 2, '|', sizeof filename);
1426 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1427 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1428 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1429 ret = CtdlIPCEndDownload(ipc, cret);
1431 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1432 filename, mimetype);
1439 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1440 void (*progress_gauge_callback)
1441 (CtdlIPC*, unsigned long, unsigned long),
1450 if (!cret) return -1;
1451 if (!buf) return -1;
1452 if (*buf) return -1;
1453 if (!filename) return -1;
1454 if (ipc->downloading) return -1;
1456 aaa = (char *)malloc(strlen(filename) + 6);
1457 if (!aaa) return -1;
1459 sprintf(aaa, "OIMG %s", filename);
1460 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1462 if (ret / 100 == 2) {
1463 ipc->downloading = 1;
1464 bytes = extract_long(cret, 0);
1465 last_mod = extract_int(cret, 1);
1466 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1467 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1468 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1469 ret = CtdlIPCEndDownload(ipc, cret);
1471 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1472 filename, mimetype);
1479 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1481 void (*progress_gauge_callback)
1482 (CtdlIPC*, unsigned long, unsigned long),
1488 char MimeTestBuf[64];
1489 const char *MimeType;
1492 if (!cret) return -1;
1493 if (!save_as) return -1;
1494 if (!comment) return -1;
1495 if (!path) return -1;
1496 if (!*path) return -1;
1497 if (ipc->uploading) return -1;
1499 uploadFP = fopen(path, "r");
1500 if (!uploadFP) return -2;
1502 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1507 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1508 aaa = (char *)malloc(strlen(save_as) + strlen(MimeType) + strlen(comment) + 7);
1509 if (!aaa) return -1;
1511 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1512 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1514 if (ret / 100 == 2) {
1516 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1517 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1525 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1526 const char *save_as,
1527 void (*progress_gauge_callback)
1528 (CtdlIPC*, unsigned long, unsigned long),
1534 char MimeTestBuf[64];
1535 const char *MimeType;
1538 if (!cret) return -1;
1539 if (!save_as) return -1;
1540 if (!path && for_real) return -1;
1541 if (!*path && for_real) return -1;
1542 if (ipc->uploading) return -1;
1544 aaa = (char *)malloc(strlen(save_as) + 17);
1545 if (!aaa) return -1;
1547 uploadFP = fopen(path, "r");
1548 if (!uploadFP) return -2;
1550 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1554 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1556 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1557 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1559 if (ret / 100 == 2 && for_real) {
1561 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1562 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1570 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1575 if (!cret) return -2;
1576 if (!username) return -2;
1578 aaa = (char *)malloc(strlen(username) + 6);
1579 if (!aaa) return -1;
1581 sprintf(aaa, "QUSR %s", username);
1582 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1589 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1593 if (!cret) return -2;
1594 if (!listing) return -2;
1595 if (*listing) return -2;
1597 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1602 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1607 if (!cret) return -2;
1608 if (!name) return -2;
1610 sprintf(aaa, "CFLR %s|%d", name, for_real);
1611 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1617 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1621 if (!cret) return -1;
1622 if (floornum < 0) return -1;
1624 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1625 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1630 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1635 if (!cret) return -2;
1636 if (!floorname) return -2;
1637 if (floornum < 0) return -2;
1639 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1640 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1648 * You only need to fill out hostname, the defaults will be used if any of the
1649 * other fields are not set properly.
1651 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1652 int revision, const char *software_name, const char *hostname,
1658 if (developerid < 0 || clientid < 0 || revision < 0 ||
1662 revision = CLIENT_VERSION - 600;
1663 software_name = "Citadel (libcitadel)";
1665 if (!hostname) return -2;
1667 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1668 if (!aaa) return -1;
1670 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1671 revision, software_name, hostname);
1672 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1679 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1685 if (!cret) return -2;
1686 if (!username) return -2;
1688 aaa = (char *)malloc(strlen(username) + 8);
1689 if (!aaa) return -1;
1692 sprintf(aaa, "SEXP %s|-", username);
1693 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1696 sprintf(aaa, "SEXP %s||", username);
1697 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1705 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1709 if (!cret) return -2;
1710 if (!listing) return -2;
1711 if (*listing) return -2;
1713 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1718 /* mode is 0 = enable, 1 = disable, 2 = status */
1719 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1723 if (!cret) return -2;
1725 sprintf(aaa, "DEXP %d", mode);
1726 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1731 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1733 if (!cret) return -2;
1734 if (!bio) return -2;
1736 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1742 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1748 if (!cret) return -2;
1749 if (!username) return -2;
1750 if (!listing) return -2;
1751 if (*listing) return -2;
1753 aaa = (char *)malloc(strlen(username) + 6);
1754 if (!aaa) return -1;
1756 sprintf(aaa, "RBIO %s", username);
1757 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1764 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1768 if (!cret) return -2;
1769 if (!listing) return -2;
1770 if (*listing) return -2;
1772 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1777 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1781 if (!cret) return -1;
1783 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1784 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1789 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1793 if (!cret) return -1;
1795 sprintf(aaa, "TERM %d", sid);
1796 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1801 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1803 if (!cret) return -1;
1805 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1810 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1814 if (!cret) return -1;
1816 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1817 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1822 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1828 if (!cret) return -2;
1829 if (!text) return -2;
1830 if (!filename) return -2;
1832 aaa = (char *)malloc(strlen(filename) + 6);
1833 if (!aaa) return -1;
1835 sprintf(aaa, "EMSG %s", filename);
1836 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1843 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1848 if (!cret) return -2;
1849 if (!hostname) return -2;
1851 aaa = (char *)malloc(strlen(hostname) + 6);
1852 if (!aaa) return -1;
1854 sprintf(aaa, "HCHG %s", hostname);
1855 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1862 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1867 if (!cret) return -2;
1868 if (!roomname) return -2;
1870 aaa = (char *)malloc(strlen(roomname) + 6);
1871 if (!aaa) return -1;
1873 sprintf(aaa, "RCHG %s", roomname);
1874 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1881 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1886 if (!cret) return -2;
1887 if (!username) return -2;
1889 aaa = (char *)malloc(strlen(username) + 6);
1890 if (!aaa) return -1;
1892 sprintf(aaa, "UCHG %s", username);
1893 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1900 /* This function returns the actual server time reported, or 0 if error */
1901 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1906 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1907 if (ret / 100 == 2) {
1908 tret = extract_long(cret, 0);
1917 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who, struct ctdluser **uret, char *cret)
1922 if (!cret) return -2;
1923 if (!uret) return -2;
1924 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1925 if (!*uret) return -1;
1927 sprintf(aaa, "AGUP %s", who);
1928 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1930 if (ret / 100 == 2) {
1931 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1932 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1933 uret[0]->flags = extract_int(cret, 2);
1934 uret[0]->timescalled = extract_long(cret, 3);
1935 uret[0]->posted = extract_long(cret, 4);
1936 uret[0]->axlevel = extract_int(cret, 5);
1937 uret[0]->usernum = extract_long(cret, 6);
1938 uret[0]->lastcall = extract_long(cret, 7);
1939 uret[0]->USuserpurge = extract_int(cret, 8);
1946 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1951 if (!cret) return -2;
1952 if (!uret) return -2;
1954 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1955 if (!aaa) return -1;
1957 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1958 uret->fullname, uret->password, uret->flags, uret->timescalled,
1959 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge
1961 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1968 int CtdlIPCAideGetEmailAddresses(CtdlIPC *ipc, const char *who, char *target_buf, char *cret)
1972 char *emailaddrs = NULL;
1973 size_t emailaddrs_len = 0;
1975 sprintf(aaa, "AGEA %s", who);
1976 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
1978 if (ret / 100 == 1) {
1979 strcpy(target_buf, emailaddrs);
1982 if (emailaddrs != NULL) {
1991 int CtdlIPCAideSetEmailAddresses(CtdlIPC *ipc, const char *who, char *emailaddrs, char *cret)
1996 if (!who) return -2;
1997 if (!emailaddrs) return -2;
1998 if (!cret) return -2;
2000 sprintf(aaa, "ASEA %s", who);
2001 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2007 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2008 /* caller must free the struct ExpirePolicy */
2009 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
2010 struct ExpirePolicy **policy, char *cret)
2012 static char *proto[] = {
2016 strof(mailboxespolicy)
2021 if (!cret) return -2;
2022 if (!policy) return -2;
2023 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
2024 if (!*policy) return -1;
2025 if (which < 0 || which > 3) return -2;
2027 sprintf(cmd, "GPEX %s", proto[which]);
2028 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2029 if (ret / 100 == 2) {
2030 policy[0]->expire_mode = extract_int(cret, 0);
2031 policy[0]->expire_value = extract_int(cret, 1);
2038 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2039 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2040 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
2041 struct ExpirePolicy *policy, char *cret)
2044 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2046 if (!cret) return -2;
2047 if (which < 0 || which > 3) return -2;
2048 if (!policy) return -2;
2049 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
2050 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
2052 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
2053 policy->expire_mode, policy->expire_value);
2054 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2059 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
2063 if (!cret) return -2;
2064 if (!listing) return -2;
2065 if (*listing) return -2;
2067 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
2068 listing, &bytes, cret);
2073 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
2075 if (!cret) return -2;
2076 if (!listing) return -2;
2078 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
2084 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype, char **listing, char *cret)
2090 if (!cret) return -2;
2091 if (!mimetype) return -2;
2092 if (!listing) return -2;
2093 if (*listing) return -2;
2095 aaa = malloc(strlen(mimetype) + 13);
2096 if (!aaa) return -1;
2097 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2098 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2105 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype, const char *listing, char *cret)
2110 if (!cret) return -2;
2111 if (!mimetype) return -2;
2112 if (!listing) return -2;
2114 aaa = malloc(strlen(mimetype) + 13);
2115 if (!aaa) return -1;
2116 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2117 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2124 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
2128 if (!cret) return -2;
2129 if (!listing) return -2;
2130 if (*listing) return -2;
2132 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2137 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
2139 if (!cret) return -2;
2140 if (!listing) return -2;
2142 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2147 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2151 if (!cret) return -2;
2152 if (session < 0) return -2;
2154 sprintf(aaa, "REQT %d", session);
2155 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2160 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2164 if (!cret) return -2;
2165 if (msgnum < 0) return -2;
2167 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2168 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2173 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2182 /* New SSL object */
2183 temp_ssl = SSL_new(ssl_ctx);
2185 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2188 /* Pointless flag waving */
2189 #if SSLEAY_VERSION_NUMBER >= 0x0922
2190 SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
2193 /* Associate network connection with SSL object */
2194 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2195 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2199 if (status_hook != NULL) {
2200 status_hook("Requesting encryption...\r");
2203 /* Ready to start SSL/TLS */
2204 r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2206 error_printf("Server can't start TLS: %s\n", buf);
2211 /* Do SSL/TLS handshake */
2212 if ((a = SSL_connect(temp_ssl)) < 1) {
2213 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2217 ipc->ssl = temp_ssl;
2219 error_printf("Encrypting with %s cipher %s\n",
2220 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2221 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2226 #endif /* HAVE_OPENSSL */
2231 static void endtls(SSL *ssl)
2242 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2247 if (!address) return -2;
2248 if (!cret) return -2;
2250 aaa = (char *)malloc(strlen(address) + 6);
2251 if (!aaa) return -1;
2253 sprintf(aaa, "QDIR %s", address);
2254 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2261 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2265 if (!cret) return -2;
2266 sprintf(aaa, "IPGM %d", secret);
2267 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2273 /* ************************************************************************** */
2274 /* Stuff below this line is not for public consumption */
2275 /* ************************************************************************** */
2278 /* Read a listing from the server up to 000. Append to dest if it exists */
2279 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2288 length = strlen(ret);
2293 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2294 linelength = strlen(aaa);
2295 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2297 strcpy(&ret[length], aaa);
2298 length += linelength;
2299 strcpy(&ret[length++], "\n");
2307 /* Send a listing to the server; generate the ending 000. */
2308 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2312 text = (char *)malloc(strlen(listing) + 6);
2314 strcpy(text, listing);
2315 while (text[strlen(text) - 1] == '\n')
2316 text[strlen(text) - 1] = '\0';
2317 strcat(text, "\n000");
2318 CtdlIPC_putline(ipc, text);
2322 /* Malloc failed but we are committed to send */
2323 /* This may result in extra blanks at the bottom */
2324 CtdlIPC_putline(ipc, text);
2325 CtdlIPC_putline(ipc, "000");
2331 /* Partial read of file from server */
2332 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2338 if (!cret) return 0;
2339 if (bytes < 1) return 0;
2342 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2343 CtdlIPC_putline(ipc, aaa);
2344 CtdlIPC_getline(ipc, aaa);
2346 strcpy(cret, &aaa[4]);
2348 len = extract_long(&aaa[4], 0);
2349 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2351 /* I know what I'm doing */
2352 serv_read(ipc, ((char *)(*buf) + offset), len);
2354 /* We have to read regardless */
2355 serv_read(ipc, aaa, len);
2359 CtdlIPC_unlock(ipc);
2365 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2369 if (!cret) return -2;
2370 if (!ipc->downloading) return -2;
2372 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2374 ipc->downloading = 0;
2380 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2384 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2385 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2392 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2393 void (*progress_gauge_callback)
2394 (CtdlIPC*, unsigned long, unsigned long),
2399 if (!cret) return -1;
2400 if (!buf) return -1;
2401 if (*buf) return -1;
2402 if (!ipc->downloading) return -1;
2405 if (progress_gauge_callback)
2406 progress_gauge_callback(ipc, len, bytes);
2407 while (len < bytes) {
2410 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2416 if (progress_gauge_callback)
2417 progress_gauge_callback(ipc, len, bytes);
2422 /* READ - pipelined */
2423 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2425 void (*progress_gauge_callback)
2426 (CtdlIPC*, unsigned long, unsigned long),
2430 int calls; /* How many calls in the pipeline */
2431 int i; /* iterator */
2434 if (!cret) return -1;
2435 if (!buf) return -1;
2436 if (*buf) return -1;
2437 if (!ipc->downloading) return -1;
2439 *buf = (void *)realloc(*buf, bytes - resume);
2440 if (!*buf) return -1;
2444 if (progress_gauge_callback)
2445 progress_gauge_callback(ipc, len, bytes);
2447 /* How many calls will be in the pipeline? */
2448 calls = (bytes - resume) / 4096;
2449 if ((bytes - resume) % 4096) calls++;
2451 /* Send all requests at once */
2452 for (i = 0; i < calls; i++) {
2453 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2454 CtdlIPC_putline(ipc, aaa);
2457 /* Receive all responses at once */
2458 for (i = 0; i < calls; i++) {
2459 CtdlIPC_getline(ipc, aaa);
2461 strcpy(cret, &aaa[4]);
2463 len = extract_long(&aaa[4], 0);
2464 /* I know what I'm doing */
2465 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2467 if (progress_gauge_callback)
2468 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2470 CtdlIPC_unlock(ipc);
2476 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2481 if (!cret) return -1;
2482 if (!ipc->uploading) return -1;
2484 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2485 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2492 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2493 void (*progress_gauge_callback)
2494 (CtdlIPC*, unsigned long, unsigned long),
2502 FILE *fd = uploadFP;
2505 if (!cret) return -1;
2507 fseek(fd, 0L, SEEK_END);
2511 if (progress_gauge_callback)
2512 progress_gauge_callback(ipc, 0, bytes);
2514 while (offset < bytes) {
2517 /* Read some data in */
2518 to_write = fread(buf, 1, 4096, fd);
2520 if (feof(fd) || ferror(fd)) break;
2522 sprintf(aaa, "WRIT %d", (int)to_write);
2523 CtdlIPC_putline(ipc, aaa);
2524 CtdlIPC_getline(ipc, aaa);
2525 strcpy(cret, &aaa[4]);
2527 if (aaa[0] == '7') {
2528 to_write = extract_long(&aaa[4], 0);
2530 serv_write(ipc, buf, to_write);
2532 if (progress_gauge_callback)
2533 progress_gauge_callback(ipc, offset, bytes);
2534 /* Detect short reads and back up if needed */
2535 /* offset will never be negative anyway */
2536 fseek(fd, (signed)offset, SEEK_SET);
2541 if (progress_gauge_callback)
2542 progress_gauge_callback(ipc, 1, 1);
2545 return (!ferr ? ret : -2);
2550 * Generic command method. This method should handle any server command
2551 * except for CHAT. It takes the following arguments:
2553 * ipc The server to speak with
2554 * command Preformatted command to send to server
2555 * to_send A text or binary file to send to server
2556 * (only sent if server requests it)
2557 * bytes_to_send The number of bytes in to_send (required if
2558 * sending binary, optional if sending listing)
2559 * to_receive Pointer to a NULL pointer, if the server
2560 * sends text or binary we will allocate memory
2561 * for the file and stuff it here
2562 * bytes_to_receive If a file is received, we will store its
2564 * proto_response The protocol response. Caller must provide
2565 * this buffer and ensure that it is at least
2566 * 128 bytes in length.
2568 * This function returns a number equal to the protocol response number,
2569 * -1 if an internal error occurred, -2 if caller provided bad values,
2570 * or 0 - the protocol response number if bad values were found during
2571 * the protocol exchange.
2572 * It stores the protocol response string (minus the number) in
2573 * protocol_response as described above. Some commands send additional
2574 * data in this string.
2576 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2577 const char *command, const char *to_send,
2578 size_t bytes_to_send, char **to_receive,
2579 size_t *bytes_to_receive, char *proto_response)
2584 if (!command) return -2;
2585 if (!proto_response) return -2;
2588 CtdlIPC_putline(ipc, command);
2590 CtdlIPC_getline(ipc, proto_response);
2591 if (proto_response[3] == '*')
2593 ret = atoi(proto_response);
2594 strcpy(proto_response, &proto_response[4]);
2595 switch (ret / 100) {
2596 default: /* Unknown, punt */
2598 case 3: /* MORE_DATA */
2600 /* Don't need to do anything */
2602 case 1: /* LISTING_FOLLOWS */
2603 if (to_receive && !*to_receive && bytes_to_receive) {
2604 *to_receive = CtdlIPCReadListing(ipc, NULL);
2605 } else { /* Drain */
2606 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2610 case 4: /* SEND_LISTING */
2612 CtdlIPCSendListing(ipc, to_send);
2614 /* No listing given, fake it */
2615 CtdlIPC_putline(ipc, "000");
2619 case 6: /* BINARY_FOLLOWS */
2620 if (to_receive && !*to_receive && bytes_to_receive) {
2622 extract_long(proto_response, 0);
2623 *to_receive = (char *)
2624 malloc((size_t)*bytes_to_receive);
2628 serv_read(ipc, *to_receive,
2635 drain = extract_long(proto_response, 0);
2636 while (drain > SIZ) {
2637 serv_read(ipc, buf, SIZ);
2640 serv_read(ipc, buf, drain);
2644 case 7: /* SEND_BINARY */
2645 if (to_send && bytes_to_send) {
2646 serv_write(ipc, to_send, bytes_to_send);
2647 } else if (bytes_to_send) {
2648 /* Fake it, send nulls */
2651 fake = bytes_to_send;
2652 memset(buf, '\0', SIZ);
2653 while (fake > SIZ) {
2654 serv_write(ipc, buf, SIZ);
2657 serv_write(ipc, buf, fake);
2659 } /* else who knows? DANGER WILL ROBINSON */
2661 case 8: /* START_CHAT_MODE */
2662 if (!strncasecmp(command, "CHAT", 4)) {
2663 /* Don't call chatmode with generic! */
2664 CtdlIPC_putline(ipc, "/quit");
2667 /* In this mode we send then receive listing */
2669 CtdlIPCSendListing(ipc, to_send);
2671 /* No listing given, fake it */
2672 CtdlIPC_putline(ipc, "000");
2675 if (to_receive && !*to_receive
2676 && bytes_to_receive) {
2677 *to_receive = CtdlIPCReadListing(ipc, NULL);
2678 } else { /* Drain */
2679 while (CtdlIPC_getline(ipc, buf),
2680 strcmp(buf, "000")) ;
2685 case 9: /* ASYNC_MSG */
2686 /* CtdlIPCDoAsync(ret, proto_response); */
2687 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2693 CtdlIPC_unlock(ipc);
2699 * Connect to a Citadel on a remote host using a TCP/IP socket
2701 static int tcp_connectsock(char *host, char *service)
2703 struct in6_addr serveraddr;
2704 struct addrinfo hints;
2705 struct addrinfo *res = NULL;
2706 struct addrinfo *ai = NULL;
2710 if ((host == NULL) || IsEmptyStr(host)) {
2711 service = DEFAULT_HOST ;
2713 if ((service == NULL) || IsEmptyStr(service)) {
2714 service = DEFAULT_PORT ;
2717 memset(&hints, 0x00, sizeof(hints));
2718 hints.ai_flags = AI_NUMERICSERV;
2719 hints.ai_family = AF_UNSPEC;
2720 hints.ai_socktype = SOCK_STREAM;
2723 * Handle numeric IPv4 and IPv6 addresses
2725 rc = inet_pton(AF_INET, host, &serveraddr);
2726 if (rc == 1) { /* dotted quad */
2727 hints.ai_family = AF_INET;
2728 hints.ai_flags |= AI_NUMERICHOST;
2731 rc = inet_pton(AF_INET6, host, &serveraddr);
2732 if (rc == 1) { /* IPv6 address */
2733 hints.ai_family = AF_INET6;
2734 hints.ai_flags |= AI_NUMERICHOST;
2738 /* Begin the connection process */
2740 rc = getaddrinfo(host, service, &hints, &res);
2746 * Try all available addresses until we connect to one or until we run out.
2748 for (ai = res; ai != NULL; ai = ai->ai_next) {
2749 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2750 if (sock < 0) return(-1);
2752 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2754 return(sock); /* Connected! */
2757 close(sock); /* Failed. Close the socket to avoid fd leak! */
2769 * Connect to a Citadel on the local host using a unix domain socket
2771 static int uds_connectsock(int *isLocal, char *sockpath)
2773 struct sockaddr_un addr;
2776 memset(&addr, 0, sizeof(addr));
2777 addr.sun_family = AF_UNIX;
2778 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2780 s = socket(AF_UNIX, SOCK_STREAM, 0);
2785 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2796 * input binary data from socket
2798 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2800 unsigned int len, rlen;
2802 #if defined(HAVE_OPENSSL)
2804 serv_read_ssl(ipc, buf, bytes);
2809 while (len < bytes) {
2810 rlen = read(ipc->sock, &buf[len], bytes - len);
2812 connection_died(ipc, 0);
2821 * send binary to server
2823 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2825 unsigned int bytes_written = 0;
2828 #if defined(HAVE_OPENSSL)
2830 serv_write_ssl(ipc, buf, nbytes);
2834 while (bytes_written < nbytes) {
2835 retval = write(ipc->sock, &buf[bytes_written],
2836 nbytes - bytes_written);
2838 connection_died(ipc, 0);
2841 bytes_written += retval;
2848 * input binary data from encrypted connection
2850 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2856 while (len < bytes) {
2857 if (SSL_want_read(ipc->ssl)) {
2858 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2859 error_printf("SSL_write in serv_read:\n");
2860 ERR_print_errors_fp(stderr);
2863 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2867 errval = SSL_get_error(ipc->ssl, rlen);
2868 if (errval == SSL_ERROR_WANT_READ ||
2869 errval == SSL_ERROR_WANT_WRITE) {
2874 Not sure why we'd want to handle these error codes any differently,
2875 but this definitely isn't the way to handle them. Someone must have
2876 naively assumed that we could fall back to unencrypted communications,
2877 but all it does is just recursively blow the stack.
2878 if (errval == SSL_ERROR_ZERO_RETURN ||
2879 errval == SSL_ERROR_SSL) {
2880 serv_read(ipc, &buf[len], bytes - len);
2884 error_printf("SSL_read in serv_read: %s\n",
2885 ERR_reason_error_string(ERR_peek_error()));
2886 connection_died(ipc, 1);
2895 * send binary to server encrypted
2897 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2899 unsigned int bytes_written = 0;
2903 while (bytes_written < nbytes) {
2904 if (SSL_want_write(ipc->ssl)) {
2905 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2906 error_printf("SSL_read in serv_write:\n");
2907 ERR_print_errors_fp(stderr);
2910 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2911 nbytes - bytes_written);
2915 errval = SSL_get_error(ipc->ssl, retval);
2916 if (errval == SSL_ERROR_WANT_READ ||
2917 errval == SSL_ERROR_WANT_WRITE) {
2921 if (errval == SSL_ERROR_ZERO_RETURN ||
2922 errval == SSL_ERROR_SSL) {
2923 serv_write(ipc, &buf[bytes_written],
2924 nbytes - bytes_written);
2927 error_printf("SSL_write in serv_write: %s\n",
2928 ERR_reason_error_string(ERR_peek_error()));
2929 connection_died(ipc, 1);
2932 bytes_written += retval;
2937 #ifdef THREADED_CLIENT
2938 static void ssl_lock(int mode, int n, const char *file, int line)
2940 if (mode & CRYPTO_LOCK)
2941 pthread_mutex_lock(Critters[n]);
2943 pthread_mutex_unlock(Critters[n]);
2945 #endif /* THREADED_CLIENT */
2948 static void CtdlIPC_init_OpenSSL(void)
2951 const SSL_METHOD *ssl_method;
2954 /* already done init */
2963 SSL_load_error_strings();
2964 SSLeay_add_ssl_algorithms();
2966 /* Set up the SSL context in which we will oeprate */
2967 ssl_method = SSLv23_client_method();
2968 ssl_ctx = SSL_CTX_new(ssl_method);
2970 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2973 /* Any reasonable cipher we can get */
2974 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2975 error_printf("No ciphers available for encryption\n");
2978 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2980 /* Load DH parameters into the context */
2983 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
2987 if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
2988 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
2993 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2996 #ifdef THREADED_CLIENT
2997 /* OpenSSL requires callbacks for threaded clients */
2998 CRYPTO_set_locking_callback(ssl_lock);
2999 CRYPTO_set_id_callback(id_callback);
3001 /* OpenSSL requires us to do semaphores for threaded clients */
3002 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
3004 perror("malloc failed");
3007 for (a = 0; a < CRYPTO_num_locks(); a++) {
3008 Critters[a] = malloc(sizeof (pthread_mutex_t));
3010 perror("malloc failed");
3013 pthread_mutex_init(Critters[a], NULL);
3016 #endif /* THREADED_CLIENT */
3021 #ifdef THREADED_CLIENT
3022 static unsigned long id_callback(void) {
3023 return (unsigned long)pthread_self();
3025 #endif /* THREADED_CLIENT */
3026 #endif /* HAVE_OPENSSL */
3030 ReadNetworkChunk(CtdlIPC* ipc)
3046 FD_SET(ipc->sock, &read_fd);
3047 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
3051 *(ipc->BufPtr) = '\0';
3052 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3054 ipc->BufPtr[n]='\0';
3062 if (!(errno == EINTR || errno == EAGAIN))
3063 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
3069 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3071 ipc->BufPtr[n]='\0';
3076 connection_died(ipc, 0);
3084 * input string from socket - implemented in terms of serv_read()
3088 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3091 char *aptr, *bptr, *aeptr, *beptr;
3093 // error_printf("---\n");
3096 #if defined(HAVE_OPENSSL)
3099 /* Read one character at a time. */
3101 serv_read(ipc, &buf[i], 1);
3102 if (buf[i] == '\n' || i == (SIZ-1))
3106 /* If we got a long line, discard characters until the newline. */
3108 while (buf[i] != '\n')
3109 serv_read(ipc, &buf[i], 1);
3111 /* Strip the trailing newline (and carriage return, if present) */
3112 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3113 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3118 if (ipc->Buf == NULL)
3121 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3123 ipc->BufPtr = ipc->Buf;
3127 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3128 if (ipc->BufUsed == 0)
3129 ReadNetworkChunk(ipc);
3131 //// if (ipc->BufUsed != 0) while (1)
3137 aeptr = ipc->Buf + ipc->BufSize;
3138 while ((aptr < aeptr) &&
3142 *(bptr++) = *(aptr++);
3143 if ((*aptr == '\n') && (aptr < aeptr))
3145 /* Terminate it right, remove the line breaks */
3146 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3148 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3151 // 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);
3152 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3155 /* is there more in the buffer we need to read later? */
3156 if (ipc->Buf + ipc->BufUsed > aptr)
3163 ipc->BufPtr = ipc->Buf;
3165 // error_printf("----bla6\n");
3168 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3169 else if ((ipc->BufPtr != ipc->Buf) &&
3170 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3172 size_t NewBufSize = ipc->BufSize * 2;
3173 int delta = (ipc->BufPtr - ipc->Buf);
3176 /* if the line would end after our buffer, we should use a bigger buffer. */
3177 NewBuf = (char *)malloc (NewBufSize + 10);
3178 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3180 ipc->Buf = ipc->BufPtr = NewBuf;
3181 ipc->BufUsed -= delta;
3182 ipc->BufSize = NewBufSize;
3184 if (ReadNetworkChunk(ipc) <0)
3186 // error_printf("----bla\n");
3190 /// error_printf("----bl45761%s\nipc->BufUsed");
3192 // error_printf("----bla1\n");
3195 #else /* CHUNKED_READ */
3197 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3201 /* Read one character at a time. */
3203 serv_read(ipc, &buf[i], 1);
3204 if (buf[i] == '\n' || i == (SIZ-1))
3208 /* If we got a long line, discard characters until the newline. */
3210 while (buf[i] != '\n')
3211 serv_read(ipc, &buf[i], 1);
3213 /* Strip the trailing newline (and carriage return, if present) */
3214 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3215 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3219 #endif /* CHUNKED_READ */
3222 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3224 CtdlIPC_getline(ipc, buf);
3228 * send line to server - implemented in terms of serv_write()
3230 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3236 cmd = malloc(len + 2);
3238 /* This requires no extra memory */
3239 serv_write(ipc, buf, len);
3240 serv_write(ipc, "\n", 1);
3242 /* This is network-optimized */
3243 strncpy(cmd, buf, len);
3244 strcpy(cmd + len, "\n");
3245 serv_write(ipc, cmd, len + 1);
3249 ipc->last_command_sent = time(NULL);
3252 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3254 CtdlIPC_putline(ipc, buf);
3261 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3269 ipc = ialloc(CtdlIPC);
3273 #if defined(HAVE_OPENSSL)
3275 CtdlIPC_init_OpenSSL();
3277 #if defined(HAVE_PTHREAD_H)
3278 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3280 ipc->sock = -1; /* Not connected */
3281 ipc->isLocal = 0; /* Not local, of course! */
3282 ipc->downloading = 0;
3284 ipc->last_command_sent = 0L;
3285 ipc->network_status_cb = NULL;
3290 strcpy(cithost, DEFAULT_HOST); /* default host */
3291 strcpy(citport, DEFAULT_PORT); /* default port */
3293 /* Allow caller to supply our values */
3294 if (hostbuf && strlen(hostbuf) > 0) {
3295 strcpy(cithost, hostbuf);
3297 if (portbuf && strlen(portbuf) > 0) {
3298 strcpy(citport, portbuf);
3301 /* Read host/port from command line if present */
3302 for (a = 0; a < argc; ++a) {
3305 } else if (a == 1) {
3306 strcpy(cithost, argv[a]);
3307 } else if (a == 2) {
3308 strcpy(citport, argv[a]);
3310 error_printf("%s: usage: ",argv[0]);
3311 error_printf("%s [host] [port] ",argv[0]);
3318 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3322 /* If we're using a unix domain socket we can do a bunch of stuff */
3323 if (!strcmp(cithost, UDS)) {
3324 if (!strcasecmp(citport, DEFAULT_PORT)) {
3325 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3328 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3330 printf("[%s]\n", sockpath);
3331 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3332 if (ipc->sock == -1) {
3336 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3337 if (portbuf != NULL) strcpy(portbuf, sockpath);
3338 strcpy(ipc->ip_hostname, "");
3339 strcpy(ipc->ip_address, "");
3343 printf("[%s:%s]\n", cithost, citport);
3344 ipc->sock = tcp_connectsock(cithost, citport);
3345 if (ipc->sock == -1) {
3351 /* Learn the actual network identity of the host to which we are connected */
3353 struct sockaddr_in6 clientaddr;
3354 unsigned int addrlen = sizeof(clientaddr);
3356 ipc->ip_hostname[0] = 0;
3357 ipc->ip_address[0] = 0;
3359 getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3360 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3361 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3363 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3364 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3367 /* stuff other things elsewhere */
3369 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3370 if (portbuf != NULL) strcpy(portbuf, citport);
3376 * Disconnect and delete the IPC class (destructor)
3378 void CtdlIPC_delete(CtdlIPC* ipc)
3382 SSL_shutdown(ipc->ssl);
3387 if (ipc->sock > -1) {
3388 shutdown(ipc->sock, 2); /* Close it up */
3391 if (ipc->Buf != NULL)
3400 * Disconnect and delete the IPC class (destructor)
3401 * Also NULLs out the pointer
3403 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3405 CtdlIPC_delete(*pipc);
3411 * return the file descriptor of the server socket so we can select() on it.
3413 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3416 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3423 * return one character
3425 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3428 char CtdlIPC_get(CtdlIPC* ipc)
3433 serv_read(ipc, buf, 1);