2 * Copyright (c) 1987-2017 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 register 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 register 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, "hnod=", 5))
661 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
662 else if (!strncasecmp(aaa, "room=", 5))
663 safestrncpy(mret[0]->room, &aaa[5], SIZ);
664 else if (!strncasecmp(aaa, "node=", 5))
665 safestrncpy(mret[0]->node, &aaa[5], SIZ);
666 else if (!strncasecmp(aaa, "rcpt=", 5))
667 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
668 else if (!strncasecmp(aaa, "wefw=", 5))
669 safestrncpy(mret[0]->references, &aaa[5], SIZ);
670 else if (!strncasecmp(aaa, "time=", 5))
671 mret[0]->time = atol(&aaa[5]);
673 /* Multipart/alternative prefix & suffix strings help
674 * us to determine which part we want to download.
676 else if (!strncasecmp(aaa, "pref=", 5)) {
677 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
678 if (!strcasecmp(multipart_prefix,
679 "multipart/alternative")) {
683 else if (!strncasecmp(aaa, "suff=", 5)) {
684 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
685 if (!strcasecmp(multipart_prefix,
686 "multipart/alternative")) {
691 else if (!strncasecmp(aaa, "part=", 5)) {
692 struct parts *ptr, *chain;
694 ptr = (struct parts *)calloc(1, sizeof (struct parts));
697 /* Fill the buffers for the caller */
698 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
699 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
700 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
701 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
702 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
703 ptr->length = extract_long(&aaa[5], 5);
704 if (!mret[0]->attachments)
705 mret[0]->attachments = ptr;
707 chain = mret[0]->attachments;
713 /* Now handle multipart/alternative */
714 if (multipart_hunting > 0) {
715 if ( (!strcasecmp(ptr->mimetype,
717 || (!strcasecmp(ptr->mimetype,
719 strcpy(mret[0]->mime_chosen,
727 /* Eliminate "text\n" */
728 remove_token(bbb, 0, '\n');
730 /* If doing a MIME thing, pull out the extra headers */
733 if (!strncasecmp(bbb, "Content-type:", 13)) {
734 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
735 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
736 striplt(mret[0]->content_type);
738 /* strip out ";charset=" portion. FIXME do something with
739 * the charset (like... convert it) instead of just throwing
742 if (strstr(mret[0]->content_type, ";") != NULL) {
743 strcpy(strstr(mret[0]->content_type, ";"), "");
747 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
748 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
749 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
750 striplt(mret[0]->mime_chosen);
752 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
753 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
754 strcpy(encoding, &encoding[26]);
757 remove_token(bbb, 0, '\n');
758 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
759 remove_token(bbb, 0, '\n');
766 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
768 int bytes_decoded = 0;
769 ccc = malloc(strlen(bbb) + 32768);
770 if (!strcasecmp(encoding, "base64")) {
771 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
773 else if (!strcasecmp(encoding, "quoted-printable")) {
774 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
776 ccc[bytes_decoded] = 0;
781 /* FIXME: Strip trailing whitespace */
782 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
785 bbb = (char *)realloc(bbb, 1);
795 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
800 if (!cret) return -2;
801 if (!listing) return -2;
802 if (*listing) return -2;
804 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
810 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
814 char *listing = NULL;
817 if (!cret) return -2;
819 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
820 if (ret / 100 == 1) {
823 while (*listing && strlen(listing)) {
824 extract_token(buf, listing, 0, '\n', sizeof buf);
825 remove_token(listing, 0, '\n');
827 case 0: ipc->ServInfo.pid = atoi(buf);
829 case 1: strcpy(ipc->ServInfo.nodename,buf);
831 case 2: strcpy(ipc->ServInfo.humannode,buf);
833 case 3: strcpy(ipc->ServInfo.fqdn,buf);
835 case 4: strcpy(ipc->ServInfo.software,buf);
837 case 5: ipc->ServInfo.rev_level = atoi(buf);
839 case 6: strcpy(ipc->ServInfo.site_location,buf);
841 case 7: strcpy(ipc->ServInfo.sysadm,buf);
843 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
845 case 10: ipc->ServInfo.ok_floors = atoi(buf);
847 case 11: ipc->ServInfo.paging_level = atoi(buf);
849 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
851 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
853 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
855 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
857 case 17: ipc->ServInfo.load_avg = atof(buf);
859 case 18: ipc->ServInfo.worker_avg = atof(buf);
861 case 19: ipc->ServInfo.thread_count = atoi(buf);
863 case 20: ipc->ServInfo.has_sieve = atoi(buf);
865 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
867 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
869 case 24: ipc->ServInfo.guest_logins = atoi(buf);
875 if (listing) free(listing);
881 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
886 if (!cret) return -2;
887 if (!listing) return -2;
888 if (*listing) return -2;
890 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
896 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
898 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
903 if (!cret) return -2;
906 sprintf(aaa, "SLRP %ld", msgnum);
909 sprintf(aaa, "SLRP HIGHEST");
911 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
917 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
922 if (!cret) return -2;
923 if (!username) return -2;
925 aaa = (char *)malloc(strlen(username) + 6);
928 sprintf(aaa, "INVT %s", username);
929 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
936 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
941 if (!cret) return -1;
942 if (!username) return -1;
944 aaa = (char *)malloc(strlen(username) + 6);
946 sprintf(aaa, "KICK %s", username);
947 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
954 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
958 if (!cret) return -2;
959 if (!qret) return -2;
960 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
961 if (!*qret) return -1;
963 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
964 if (ret / 100 == 2) {
965 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
966 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
967 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
968 qret[0]->QRflags = extract_int(cret, 3);
969 qret[0]->QRfloor = extract_int(cret, 4);
970 qret[0]->QRorder = extract_int(cret, 5);
971 qret[0]->QRdefaultview = extract_int(cret, 6);
972 qret[0]->QRflags2 = extract_int(cret, 7);
979 /* set forget to kick all users out of room */
980 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
985 if (!cret) return -2;
986 if (!qret) return -2;
988 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
989 strlen(qret->QRdirname) + 64);
992 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
993 qret->QRname, qret->QRpasswd, qret->QRdirname,
994 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
995 qret->QRdefaultview, qret->QRflags2);
996 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1003 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
1005 if (!cret) return -1;
1007 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
1012 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
1017 if (!cret) return -2;
1018 if (!username) return -2;
1020 aaa = (char *)malloc(strlen(username) + 6);
1021 if (!aaa) return -1;
1023 sprintf(aaa, "SETA %s", username);
1024 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1031 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
1037 if (!cret) return -2;
1040 if (mr->references) {
1041 for (ptr=mr->references; *ptr != 0; ++ptr) {
1042 if (*ptr == '|') *ptr = '!';
1046 snprintf(cmd, sizeof cmd,
1047 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
1048 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
1049 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
1051 if ((flag == 0) && (subject_required != NULL)) {
1052 /* Is the server strongly recommending that the user enter a message subject? */
1053 if ((cret[3] != '\0') && (cret[4] != '\0')) {
1054 *subject_required = extract_int(&cret[4], 1);
1064 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
1068 if (!cret) return -2;
1069 if (!iret) return -2;
1070 if (*iret) return -2;
1072 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
1077 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
1081 if (!cret) return -2;
1082 if (!msgnum) return -2;
1084 sprintf(aaa, "DELE %ld", msgnum);
1085 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1090 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
1095 if (!cret) return -2;
1096 if (!destroom) return -2;
1097 if (!msgnum) return -2;
1099 aaa = (char *)malloc(strlen(destroom) + 28);
1100 if (!aaa) return -1;
1102 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
1103 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1110 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
1114 if (!cret) return -2;
1116 sprintf(aaa, "KILL %d", for_real);
1117 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1122 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
1123 const char *password, int floor, char *cret)
1128 if (!cret) return -2;
1129 if (!roomname) return -2;
1132 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
1133 if (!aaa) return -1;
1134 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
1137 aaa = (char *)malloc(strlen(roomname) + 40);
1138 if (!aaa) return -1;
1139 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1142 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1149 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1151 if (!cret) return -2;
1153 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1158 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1164 if (!cret) return -2;
1165 if (!mret) return -2;
1166 if (*mret) return -2;
1167 if (!message) return -2;
1169 aaa = (char *)malloc(strlen(message) + 6);
1170 if (!aaa) return -1;
1172 sprintf(aaa, "MESG %s", message);
1173 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1180 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1182 if (!cret) return -2;
1184 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1189 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1195 if (!cret) return -2;
1196 if (!rret) return -2;
1197 if (*rret) return -2;
1200 aaa = (char *)malloc(strlen(username) + 6);
1202 aaa = (char *)malloc(12);
1203 if (!aaa) return -1;
1206 sprintf(aaa, "GREG %s", username);
1208 sprintf(aaa, "GREG _SELF_");
1209 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1216 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1221 if (!cret) return -2;
1222 if (!username) return -2;
1223 if (axlevel < AxDeleted || axlevel > AxAideU) return -2;
1225 aaa = (char *)malloc(strlen(username) + 17);
1226 if (!aaa) return -1;
1228 sprintf(aaa, "VALI %s|%d", username, axlevel);
1229 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1236 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1240 if (!cret) return -1;
1241 if (!info) return -1;
1243 sprintf(aaa, "EINF %d", for_real);
1244 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1249 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1255 if (!cret) return -1;
1256 if (!listing) return -1;
1257 if (*listing) return -1;
1258 if (!searchstring) return -1;
1260 cmd = malloc(strlen(searchstring) + 10);
1261 sprintf(cmd, "LIST %s", searchstring);
1263 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1270 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1272 if (!cret) return -1;
1273 if (!info) return -1;
1275 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1281 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1285 if (!cret) return -1;
1286 if (!chek) return -1;
1288 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1289 if (ret / 100 == 2) {
1290 chek->newmail = extract_long(cret, 0);
1291 chek->needregis = extract_int(cret, 1);
1292 chek->needvalid = extract_int(cret, 2);
1299 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1304 if (!cret) return -2;
1305 if (!filename) return -2;
1307 aaa = (char *)malloc(strlen(filename) + 6);
1308 if (!aaa) return -1;
1310 sprintf(aaa, "DELF %s", filename);
1311 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1318 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1323 if (!cret) return -2;
1324 if (!filename) return -2;
1325 if (!destroom) return -2;
1327 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1328 if (!aaa) return -1;
1330 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1331 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1338 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1343 if (!cret) return -1;
1344 if (!listing) return -1;
1345 if (*listing) return -1;
1347 *stamp = CtdlIPCServerTime(ipc, cret);
1349 *stamp = time(NULL);
1350 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1356 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1358 void (*progress_gauge_callback)
1359 (CtdlIPC*, unsigned long, unsigned long),
1368 if (!cret) return -2;
1369 if (!filename) return -2;
1370 if (!buf) return -2;
1371 if (*buf) return -2;
1372 if (ipc->downloading) return -2;
1374 aaa = (char *)malloc(strlen(filename) + 6);
1375 if (!aaa) return -1;
1377 sprintf(aaa, "OPEN %s", filename);
1378 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1380 if (ret / 100 == 2) {
1381 ipc->downloading = 1;
1382 bytes = extract_long(cret, 0);
1383 last_mod = extract_int(cret, 1);
1384 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1386 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1387 progress_gauge_callback, cret);
1389 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1390 progress_gauge_callback, cret);
1393 ret = CtdlIPCEndDownload(ipc, cret);
1395 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1396 filename, mimetype);
1403 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1405 void (*progress_gauge_callback)
1406 (CtdlIPC*, unsigned long, unsigned long),
1416 if (!cret) return -2;
1417 if (!buf) return -2;
1418 if (*buf) return -2;
1419 if (!part) return -2;
1420 if (!msgnum) return -2;
1421 if (ipc->downloading) return -2;
1423 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1424 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1425 if (ret / 100 == 2) {
1426 ipc->downloading = 1;
1427 bytes = extract_long(cret, 0);
1428 last_mod = extract_int(cret, 1);
1429 extract_token(filename, cret, 2, '|', sizeof filename);
1430 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1431 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1432 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1433 ret = CtdlIPCEndDownload(ipc, cret);
1435 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1436 filename, mimetype);
1443 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1444 void (*progress_gauge_callback)
1445 (CtdlIPC*, unsigned long, unsigned long),
1454 if (!cret) return -1;
1455 if (!buf) return -1;
1456 if (*buf) return -1;
1457 if (!filename) return -1;
1458 if (ipc->downloading) return -1;
1460 aaa = (char *)malloc(strlen(filename) + 6);
1461 if (!aaa) return -1;
1463 sprintf(aaa, "OIMG %s", filename);
1464 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1466 if (ret / 100 == 2) {
1467 ipc->downloading = 1;
1468 bytes = extract_long(cret, 0);
1469 last_mod = extract_int(cret, 1);
1470 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1471 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1472 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1473 ret = CtdlIPCEndDownload(ipc, cret);
1475 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1476 filename, mimetype);
1483 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1485 void (*progress_gauge_callback)
1486 (CtdlIPC*, unsigned long, unsigned long),
1492 char MimeTestBuf[64];
1493 const char *MimeType;
1496 if (!cret) return -1;
1497 if (!save_as) return -1;
1498 if (!comment) return -1;
1499 if (!path) return -1;
1500 if (!*path) return -1;
1501 if (ipc->uploading) return -1;
1503 uploadFP = fopen(path, "r");
1504 if (!uploadFP) return -2;
1506 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1511 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1512 aaa = (char *)malloc(strlen(save_as) + strlen(MimeType) + strlen(comment) + 7);
1513 if (!aaa) return -1;
1515 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1516 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1518 if (ret / 100 == 2) {
1520 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1521 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1529 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1530 const char *save_as,
1531 void (*progress_gauge_callback)
1532 (CtdlIPC*, unsigned long, unsigned long),
1538 char MimeTestBuf[64];
1539 const char *MimeType;
1542 if (!cret) return -1;
1543 if (!save_as) return -1;
1544 if (!path && for_real) return -1;
1545 if (!*path && for_real) return -1;
1546 if (ipc->uploading) return -1;
1548 aaa = (char *)malloc(strlen(save_as) + 17);
1549 if (!aaa) return -1;
1551 uploadFP = fopen(path, "r");
1552 if (!uploadFP) return -2;
1554 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1558 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1560 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1561 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1563 if (ret / 100 == 2 && for_real) {
1565 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1566 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1574 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1579 if (!cret) return -2;
1580 if (!username) return -2;
1582 aaa = (char *)malloc(strlen(username) + 6);
1583 if (!aaa) return -1;
1585 sprintf(aaa, "QUSR %s", username);
1586 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1593 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1597 if (!cret) return -2;
1598 if (!listing) return -2;
1599 if (*listing) return -2;
1601 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1606 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1611 if (!cret) return -2;
1612 if (!name) return -2;
1614 sprintf(aaa, "CFLR %s|%d", name, for_real);
1615 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1621 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1625 if (!cret) return -1;
1626 if (floornum < 0) return -1;
1628 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1629 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1634 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1639 if (!cret) return -2;
1640 if (!floorname) return -2;
1641 if (floornum < 0) return -2;
1643 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1644 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1652 * You only need to fill out hostname, the defaults will be used if any of the
1653 * other fields are not set properly.
1655 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1656 int revision, const char *software_name, const char *hostname,
1662 if (developerid < 0 || clientid < 0 || revision < 0 ||
1666 revision = CLIENT_VERSION - 600;
1667 software_name = "Citadel (libcitadel)";
1669 if (!hostname) return -2;
1671 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1672 if (!aaa) return -1;
1674 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1675 revision, software_name, hostname);
1676 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1683 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1689 if (!cret) return -2;
1690 if (!username) return -2;
1692 aaa = (char *)malloc(strlen(username) + 8);
1693 if (!aaa) return -1;
1696 sprintf(aaa, "SEXP %s|-", username);
1697 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1700 sprintf(aaa, "SEXP %s||", username);
1701 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1709 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1713 if (!cret) return -2;
1714 if (!listing) return -2;
1715 if (*listing) return -2;
1717 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1722 /* mode is 0 = enable, 1 = disable, 2 = status */
1723 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1727 if (!cret) return -2;
1729 sprintf(aaa, "DEXP %d", mode);
1730 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1735 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1737 if (!cret) return -2;
1738 if (!bio) return -2;
1740 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1746 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1752 if (!cret) return -2;
1753 if (!username) return -2;
1754 if (!listing) return -2;
1755 if (*listing) return -2;
1757 aaa = (char *)malloc(strlen(username) + 6);
1758 if (!aaa) return -1;
1760 sprintf(aaa, "RBIO %s", username);
1761 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1768 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1772 if (!cret) return -2;
1773 if (!listing) return -2;
1774 if (*listing) return -2;
1776 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1781 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1785 if (!cret) return -1;
1787 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1788 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1793 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1797 if (!cret) return -1;
1799 sprintf(aaa, "TERM %d", sid);
1800 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1805 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1807 if (!cret) return -1;
1809 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1814 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1818 if (!cret) return -1;
1820 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1821 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1826 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1832 if (!cret) return -2;
1833 if (!text) return -2;
1834 if (!filename) return -2;
1836 aaa = (char *)malloc(strlen(filename) + 6);
1837 if (!aaa) return -1;
1839 sprintf(aaa, "EMSG %s", filename);
1840 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1847 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1852 if (!cret) return -2;
1853 if (!hostname) return -2;
1855 aaa = (char *)malloc(strlen(hostname) + 6);
1856 if (!aaa) return -1;
1858 sprintf(aaa, "HCHG %s", hostname);
1859 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1866 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1871 if (!cret) return -2;
1872 if (!roomname) return -2;
1874 aaa = (char *)malloc(strlen(roomname) + 6);
1875 if (!aaa) return -1;
1877 sprintf(aaa, "RCHG %s", roomname);
1878 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1885 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1890 if (!cret) return -2;
1891 if (!username) return -2;
1893 aaa = (char *)malloc(strlen(username) + 6);
1894 if (!aaa) return -1;
1896 sprintf(aaa, "UCHG %s", username);
1897 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1904 /* This function returns the actual server time reported, or 0 if error */
1905 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1907 register time_t tret;
1910 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1911 if (ret / 100 == 2) {
1912 tret = extract_long(cret, 0);
1921 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1922 struct ctdluser **uret, char *cret)
1927 if (!cret) return -2;
1928 if (!uret) return -2;
1929 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1930 if (!*uret) return -1;
1932 sprintf(aaa, "AGUP %s", who);
1933 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1935 if (ret / 100 == 2) {
1936 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1937 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1938 uret[0]->flags = extract_int(cret, 2);
1939 uret[0]->timescalled = extract_long(cret, 3);
1940 uret[0]->posted = extract_long(cret, 4);
1941 uret[0]->axlevel = extract_int(cret, 5);
1942 uret[0]->usernum = extract_long(cret, 6);
1943 uret[0]->lastcall = extract_long(cret, 7);
1944 uret[0]->USuserpurge = extract_int(cret, 8);
1951 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1956 if (!cret) return -2;
1957 if (!uret) return -2;
1959 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1960 if (!aaa) return -1;
1962 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1963 uret->fullname, uret->password, uret->flags,
1964 uret->timescalled, uret->posted, uret->axlevel,
1965 uret->usernum, uret->lastcall, uret->USuserpurge);
1966 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1973 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1974 /* caller must free the struct ExpirePolicy */
1975 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
1976 struct ExpirePolicy **policy, char *cret)
1978 static char *proto[] = {
1982 strof(mailboxespolicy)
1987 if (!cret) return -2;
1988 if (!policy) return -2;
1989 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1990 if (!*policy) return -1;
1991 if (which < 0 || which > 3) return -2;
1993 sprintf(cmd, "GPEX %s", proto[which]);
1994 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1995 if (ret / 100 == 2) {
1996 policy[0]->expire_mode = extract_int(cret, 0);
1997 policy[0]->expire_value = extract_int(cret, 1);
2004 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2005 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2006 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
2007 struct ExpirePolicy *policy, char *cret)
2010 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2012 if (!cret) return -2;
2013 if (which < 0 || which > 3) return -2;
2014 if (!policy) return -2;
2015 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
2016 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
2018 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
2019 policy->expire_mode, policy->expire_value);
2020 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2025 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
2029 if (!cret) return -2;
2030 if (!listing) return -2;
2031 if (*listing) return -2;
2033 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
2034 listing, &bytes, cret);
2039 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
2041 if (!cret) return -2;
2042 if (!listing) return -2;
2044 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
2050 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype, char **listing, char *cret)
2056 if (!cret) return -2;
2057 if (!mimetype) return -2;
2058 if (!listing) return -2;
2059 if (*listing) return -2;
2061 aaa = malloc(strlen(mimetype) + 13);
2062 if (!aaa) return -1;
2063 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2064 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2071 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype, const char *listing, char *cret)
2076 if (!cret) return -2;
2077 if (!mimetype) return -2;
2078 if (!listing) return -2;
2080 aaa = malloc(strlen(mimetype) + 13);
2081 if (!aaa) return -1;
2082 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2083 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2090 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
2094 if (!cret) return -2;
2095 if (!listing) return -2;
2096 if (*listing) return -2;
2098 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2103 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
2105 if (!cret) return -2;
2106 if (!listing) return -2;
2108 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2113 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2117 if (!cret) return -2;
2118 if (session < 0) return -2;
2120 sprintf(aaa, "REQT %d", session);
2121 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2126 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2130 if (!cret) return -2;
2131 if (msgnum < 0) return -2;
2133 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2134 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2139 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2148 /* New SSL object */
2149 temp_ssl = SSL_new(ssl_ctx);
2151 error_printf("SSL_new failed: %s\n",
2152 ERR_reason_error_string(ERR_get_error()));
2155 /* Pointless flag waving */
2156 #if SSLEAY_VERSION_NUMBER >= 0x0922
2157 SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
2160 if (!access(EGD_POOL, F_OK))
2163 if (!RAND_status()) {
2164 error_printf("PRNG not properly seeded\n");
2168 /* Associate network connection with SSL object */
2169 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2170 error_printf("SSL_set_fd failed: %s\n",
2171 ERR_reason_error_string(ERR_get_error()));
2175 if (status_hook != NULL)
2176 status_hook("Requesting encryption...\r");
2178 /* Ready to start SSL/TLS */
2180 CtdlIPC_putline(ipc, "STLS");
2181 CtdlIPC_getline(ipc, buf);
2182 if (buf[0] != '2') {
2183 error_printf("Server can't start TLS: %s\n", buf);
2187 r = CtdlIPCGenericCommand(ipc,
2188 "STLS", NULL, 0, NULL, NULL, cret);
2190 error_printf("Server can't start TLS: %s\n", buf);
2195 /* Do SSL/TLS handshake */
2196 if ((a = SSL_connect(temp_ssl)) < 1) {
2197 error_printf("SSL_connect failed: %s\n",
2198 ERR_reason_error_string(ERR_get_error()));
2202 ipc->ssl = temp_ssl;
2204 if (BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE))
2208 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2209 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2210 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2211 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2217 #endif /* HAVE_OPENSSL */
2222 static void endtls(SSL *ssl)
2233 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2238 if (!address) return -2;
2239 if (!cret) return -2;
2241 aaa = (char *)malloc(strlen(address) + 6);
2242 if (!aaa) return -1;
2244 sprintf(aaa, "QDIR %s", address);
2245 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2252 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2256 if (!cret) return -2;
2257 sprintf(aaa, "IPGM %d", secret);
2258 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2264 /* ************************************************************************** */
2265 /* Stuff below this line is not for public consumption */
2266 /* ************************************************************************** */
2269 /* Read a listing from the server up to 000. Append to dest if it exists */
2270 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2279 length = strlen(ret);
2284 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2285 linelength = strlen(aaa);
2286 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2288 strcpy(&ret[length], aaa);
2289 length += linelength;
2290 strcpy(&ret[length++], "\n");
2298 /* Send a listing to the server; generate the ending 000. */
2299 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2303 text = (char *)malloc(strlen(listing) + 6);
2305 strcpy(text, listing);
2306 while (text[strlen(text) - 1] == '\n')
2307 text[strlen(text) - 1] = '\0';
2308 strcat(text, "\n000");
2309 CtdlIPC_putline(ipc, text);
2313 /* Malloc failed but we are committed to send */
2314 /* This may result in extra blanks at the bottom */
2315 CtdlIPC_putline(ipc, text);
2316 CtdlIPC_putline(ipc, "000");
2322 /* Partial read of file from server */
2323 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2325 register size_t len = 0;
2329 if (!cret) return 0;
2330 if (bytes < 1) return 0;
2333 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2334 CtdlIPC_putline(ipc, aaa);
2335 CtdlIPC_getline(ipc, aaa);
2337 strcpy(cret, &aaa[4]);
2339 len = extract_long(&aaa[4], 0);
2340 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2342 /* I know what I'm doing */
2343 serv_read(ipc, ((char *)(*buf) + offset), len);
2345 /* We have to read regardless */
2346 serv_read(ipc, aaa, len);
2350 CtdlIPC_unlock(ipc);
2356 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2360 if (!cret) return -2;
2361 if (!ipc->downloading) return -2;
2363 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2365 ipc->downloading = 0;
2371 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2375 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2376 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2383 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2384 void (*progress_gauge_callback)
2385 (CtdlIPC*, unsigned long, unsigned long),
2388 register size_t len;
2390 if (!cret) return -1;
2391 if (!buf) return -1;
2392 if (*buf) return -1;
2393 if (!ipc->downloading) return -1;
2396 if (progress_gauge_callback)
2397 progress_gauge_callback(ipc, len, bytes);
2398 while (len < bytes) {
2399 register size_t block;
2401 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2407 if (progress_gauge_callback)
2408 progress_gauge_callback(ipc, len, bytes);
2413 /* READ - pipelined */
2414 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2416 void (*progress_gauge_callback)
2417 (CtdlIPC*, unsigned long, unsigned long),
2420 register size_t len;
2421 register int calls; /* How many calls in the pipeline */
2422 register int i; /* iterator */
2425 if (!cret) return -1;
2426 if (!buf) return -1;
2427 if (*buf) return -1;
2428 if (!ipc->downloading) return -1;
2430 *buf = (void *)realloc(*buf, bytes - resume);
2431 if (!*buf) return -1;
2435 if (progress_gauge_callback)
2436 progress_gauge_callback(ipc, len, bytes);
2438 /* How many calls will be in the pipeline? */
2439 calls = (bytes - resume) / 4096;
2440 if ((bytes - resume) % 4096) calls++;
2442 /* Send all requests at once */
2443 for (i = 0; i < calls; i++) {
2444 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2445 CtdlIPC_putline(ipc, aaa);
2448 /* Receive all responses at once */
2449 for (i = 0; i < calls; i++) {
2450 CtdlIPC_getline(ipc, aaa);
2452 strcpy(cret, &aaa[4]);
2454 len = extract_long(&aaa[4], 0);
2455 /* I know what I'm doing */
2456 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2458 if (progress_gauge_callback)
2459 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2461 CtdlIPC_unlock(ipc);
2467 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2472 if (!cret) return -1;
2473 if (!ipc->uploading) return -1;
2475 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2476 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2483 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2484 void (*progress_gauge_callback)
2485 (CtdlIPC*, unsigned long, unsigned long),
2488 register int ret = -1;
2489 register size_t offset = 0;
2493 FILE *fd = uploadFP;
2496 if (!cret) return -1;
2498 fseek(fd, 0L, SEEK_END);
2502 if (progress_gauge_callback)
2503 progress_gauge_callback(ipc, 0, bytes);
2505 while (offset < bytes) {
2506 register size_t to_write;
2508 /* Read some data in */
2509 to_write = fread(buf, 1, 4096, fd);
2511 if (feof(fd) || ferror(fd)) break;
2513 sprintf(aaa, "WRIT %d", (int)to_write);
2514 CtdlIPC_putline(ipc, aaa);
2515 CtdlIPC_getline(ipc, aaa);
2516 strcpy(cret, &aaa[4]);
2518 if (aaa[0] == '7') {
2519 to_write = extract_long(&aaa[4], 0);
2521 serv_write(ipc, buf, to_write);
2523 if (progress_gauge_callback)
2524 progress_gauge_callback(ipc, offset, bytes);
2525 /* Detect short reads and back up if needed */
2526 /* offset will never be negative anyway */
2527 fseek(fd, (signed)offset, SEEK_SET);
2532 if (progress_gauge_callback)
2533 progress_gauge_callback(ipc, 1, 1);
2536 return (!ferr ? ret : -2);
2541 * Generic command method. This method should handle any server command
2542 * except for CHAT. It takes the following arguments:
2544 * ipc The server to speak with
2545 * command Preformatted command to send to server
2546 * to_send A text or binary file to send to server
2547 * (only sent if server requests it)
2548 * bytes_to_send The number of bytes in to_send (required if
2549 * sending binary, optional if sending listing)
2550 * to_receive Pointer to a NULL pointer, if the server
2551 * sends text or binary we will allocate memory
2552 * for the file and stuff it here
2553 * bytes_to_receive If a file is received, we will store its
2555 * proto_response The protocol response. Caller must provide
2556 * this buffer and ensure that it is at least
2557 * 128 bytes in length.
2559 * This function returns a number equal to the protocol response number,
2560 * -1 if an internal error occurred, -2 if caller provided bad values,
2561 * or 0 - the protocol response number if bad values were found during
2562 * the protocol exchange.
2563 * It stores the protocol response string (minus the number) in
2564 * protocol_response as described above. Some commands send additional
2565 * data in this string.
2567 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2568 const char *command, const char *to_send,
2569 size_t bytes_to_send, char **to_receive,
2570 size_t *bytes_to_receive, char *proto_response)
2575 if (!command) return -2;
2576 if (!proto_response) return -2;
2579 CtdlIPC_putline(ipc, command);
2581 CtdlIPC_getline(ipc, proto_response);
2582 if (proto_response[3] == '*')
2584 ret = atoi(proto_response);
2585 strcpy(proto_response, &proto_response[4]);
2586 switch (ret / 100) {
2587 default: /* Unknown, punt */
2589 case 3: /* MORE_DATA */
2591 /* Don't need to do anything */
2593 case 1: /* LISTING_FOLLOWS */
2594 if (to_receive && !*to_receive && bytes_to_receive) {
2595 *to_receive = CtdlIPCReadListing(ipc, NULL);
2596 } else { /* Drain */
2597 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2601 case 4: /* SEND_LISTING */
2603 CtdlIPCSendListing(ipc, to_send);
2605 /* No listing given, fake it */
2606 CtdlIPC_putline(ipc, "000");
2610 case 6: /* BINARY_FOLLOWS */
2611 if (to_receive && !*to_receive && bytes_to_receive) {
2613 extract_long(proto_response, 0);
2614 *to_receive = (char *)
2615 malloc((size_t)*bytes_to_receive);
2619 serv_read(ipc, *to_receive,
2626 drain = extract_long(proto_response, 0);
2627 while (drain > SIZ) {
2628 serv_read(ipc, buf, SIZ);
2631 serv_read(ipc, buf, drain);
2635 case 7: /* SEND_BINARY */
2636 if (to_send && bytes_to_send) {
2637 serv_write(ipc, to_send, bytes_to_send);
2638 } else if (bytes_to_send) {
2639 /* Fake it, send nulls */
2642 fake = bytes_to_send;
2643 memset(buf, '\0', SIZ);
2644 while (fake > SIZ) {
2645 serv_write(ipc, buf, SIZ);
2648 serv_write(ipc, buf, fake);
2650 } /* else who knows? DANGER WILL ROBINSON */
2652 case 8: /* START_CHAT_MODE */
2653 if (!strncasecmp(command, "CHAT", 4)) {
2654 /* Don't call chatmode with generic! */
2655 CtdlIPC_putline(ipc, "/quit");
2658 /* In this mode we send then receive listing */
2660 CtdlIPCSendListing(ipc, to_send);
2662 /* No listing given, fake it */
2663 CtdlIPC_putline(ipc, "000");
2666 if (to_receive && !*to_receive
2667 && bytes_to_receive) {
2668 *to_receive = CtdlIPCReadListing(ipc, NULL);
2669 } else { /* Drain */
2670 while (CtdlIPC_getline(ipc, buf),
2671 strcmp(buf, "000")) ;
2676 case 9: /* ASYNC_MSG */
2677 /* CtdlIPCDoAsync(ret, proto_response); */
2678 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2684 CtdlIPC_unlock(ipc);
2690 * Connect to a Citadel on a remote host using a TCP/IP socket
2692 static int tcp_connectsock(char *host, char *service)
2694 struct in6_addr serveraddr;
2695 struct addrinfo hints;
2696 struct addrinfo *res = NULL;
2697 struct addrinfo *ai = NULL;
2701 if ((host == NULL) || IsEmptyStr(host)) {
2702 service = DEFAULT_HOST ;
2704 if ((service == NULL) || IsEmptyStr(service)) {
2705 service = DEFAULT_PORT ;
2708 memset(&hints, 0x00, sizeof(hints));
2709 hints.ai_flags = AI_NUMERICSERV;
2710 hints.ai_family = AF_UNSPEC;
2711 hints.ai_socktype = SOCK_STREAM;
2714 * Handle numeric IPv4 and IPv6 addresses
2716 rc = inet_pton(AF_INET, host, &serveraddr);
2717 if (rc == 1) { /* dotted quad */
2718 hints.ai_family = AF_INET;
2719 hints.ai_flags |= AI_NUMERICHOST;
2722 rc = inet_pton(AF_INET6, host, &serveraddr);
2723 if (rc == 1) { /* IPv6 address */
2724 hints.ai_family = AF_INET6;
2725 hints.ai_flags |= AI_NUMERICHOST;
2729 /* Begin the connection process */
2731 rc = getaddrinfo(host, service, &hints, &res);
2737 * Try all available addresses until we connect to one or until we run out.
2739 for (ai = res; ai != NULL; ai = ai->ai_next) {
2740 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2741 if (sock < 0) return(-1);
2743 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2745 return(sock); /* Connected! */
2748 close(sock); /* Failed. Close the socket to avoid fd leak! */
2760 * Connect to a Citadel on the local host using a unix domain socket
2762 static int uds_connectsock(int *isLocal, char *sockpath)
2764 struct sockaddr_un addr;
2767 memset(&addr, 0, sizeof(addr));
2768 addr.sun_family = AF_UNIX;
2769 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2771 s = socket(AF_UNIX, SOCK_STREAM, 0);
2776 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2787 * input binary data from socket
2789 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2791 unsigned int len, rlen;
2793 #if defined(HAVE_OPENSSL)
2795 serv_read_ssl(ipc, buf, bytes);
2800 while (len < bytes) {
2801 rlen = read(ipc->sock, &buf[len], bytes - len);
2803 connection_died(ipc, 0);
2812 * send binary to server
2814 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2816 unsigned int bytes_written = 0;
2819 #if defined(HAVE_OPENSSL)
2821 serv_write_ssl(ipc, buf, nbytes);
2825 while (bytes_written < nbytes) {
2826 retval = write(ipc->sock, &buf[bytes_written],
2827 nbytes - bytes_written);
2829 connection_died(ipc, 0);
2832 bytes_written += retval;
2839 * input binary data from encrypted connection
2841 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2847 while (len < bytes) {
2848 if (SSL_want_read(ipc->ssl)) {
2849 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2850 error_printf("SSL_write in serv_read:\n");
2851 ERR_print_errors_fp(stderr);
2854 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2858 errval = SSL_get_error(ipc->ssl, rlen);
2859 if (errval == SSL_ERROR_WANT_READ ||
2860 errval == SSL_ERROR_WANT_WRITE) {
2865 Not sure why we'd want to handle these error codes any differently,
2866 but this definitely isn't the way to handle them. Someone must have
2867 naively assumed that we could fall back to unencrypted communications,
2868 but all it does is just recursively blow the stack.
2869 if (errval == SSL_ERROR_ZERO_RETURN ||
2870 errval == SSL_ERROR_SSL) {
2871 serv_read(ipc, &buf[len], bytes - len);
2875 error_printf("SSL_read in serv_read: %s\n",
2876 ERR_reason_error_string(ERR_peek_error()));
2877 connection_died(ipc, 1);
2886 * send binary to server encrypted
2888 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2890 unsigned int bytes_written = 0;
2894 while (bytes_written < nbytes) {
2895 if (SSL_want_write(ipc->ssl)) {
2896 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2897 error_printf("SSL_read in serv_write:\n");
2898 ERR_print_errors_fp(stderr);
2901 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2902 nbytes - bytes_written);
2906 errval = SSL_get_error(ipc->ssl, retval);
2907 if (errval == SSL_ERROR_WANT_READ ||
2908 errval == SSL_ERROR_WANT_WRITE) {
2912 if (errval == SSL_ERROR_ZERO_RETURN ||
2913 errval == SSL_ERROR_SSL) {
2914 serv_write(ipc, &buf[bytes_written],
2915 nbytes - bytes_written);
2918 error_printf("SSL_write in serv_write: %s\n",
2919 ERR_reason_error_string(ERR_peek_error()));
2920 connection_died(ipc, 1);
2923 bytes_written += retval;
2928 #ifdef THREADED_CLIENT
2929 static void ssl_lock(int mode, int n, const char *file, int line)
2931 if (mode & CRYPTO_LOCK)
2932 pthread_mutex_lock(Critters[n]);
2934 pthread_mutex_unlock(Critters[n]);
2936 #endif /* THREADED_CLIENT */
2939 static void CtdlIPC_init_OpenSSL(void)
2942 const SSL_METHOD *ssl_method;
2945 /* already done init */
2954 SSL_load_error_strings();
2955 SSLeay_add_ssl_algorithms();
2957 /* Set up the SSL context in which we will oeprate */
2958 ssl_method = SSLv23_client_method();
2959 ssl_ctx = SSL_CTX_new(ssl_method);
2961 error_printf("SSL_CTX_new failed: %s\n",
2962 ERR_reason_error_string(ERR_get_error()));
2965 /* Any reasonable cipher we can get */
2966 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2967 error_printf("No ciphers available for encryption\n");
2970 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2972 /* Load DH parameters into the context */
2975 error_printf("Can't allocate a DH object: %s\n",
2976 ERR_reason_error_string(ERR_get_error()));
2979 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2980 error_printf("Can't assign DH_P: %s\n",
2981 ERR_reason_error_string(ERR_get_error()));
2985 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2986 error_printf("Can't assign DH_G: %s\n",
2987 ERR_reason_error_string(ERR_get_error()));
2992 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2995 #ifdef THREADED_CLIENT
2996 /* OpenSSL requires callbacks for threaded clients */
2997 CRYPTO_set_locking_callback(ssl_lock);
2998 CRYPTO_set_id_callback(id_callback);
3000 /* OpenSSL requires us to do semaphores for threaded clients */
3001 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
3003 perror("malloc failed");
3006 for (a = 0; a < CRYPTO_num_locks(); a++) {
3007 Critters[a] = malloc(sizeof (pthread_mutex_t));
3009 perror("malloc failed");
3012 pthread_mutex_init(Critters[a], NULL);
3015 #endif /* THREADED_CLIENT */
3020 #ifdef THREADED_CLIENT
3021 static unsigned long id_callback(void) {
3022 return (unsigned long)pthread_self();
3024 #endif /* THREADED_CLIENT */
3025 #endif /* HAVE_OPENSSL */
3029 ReadNetworkChunk(CtdlIPC* ipc)
3046 FD_SET(ipc->sock, &read_fd);
3047 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
3049 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
3053 *(ipc->BufPtr) = '\0';
3054 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3055 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3057 ipc->BufPtr[n]='\0';
3065 if (!(errno == EINTR || errno == EAGAIN))
3066 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
3072 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3074 ipc->BufPtr[n]='\0';
3079 connection_died(ipc, 0);
3087 * input string from socket - implemented in terms of serv_read()
3091 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3094 char *aptr, *bptr, *aeptr, *beptr;
3096 // error_printf("---\n");
3099 #if defined(HAVE_OPENSSL)
3102 /* Read one character at a time. */
3104 serv_read(ipc, &buf[i], 1);
3105 if (buf[i] == '\n' || i == (SIZ-1))
3109 /* If we got a long line, discard characters until the newline. */
3111 while (buf[i] != '\n')
3112 serv_read(ipc, &buf[i], 1);
3114 /* Strip the trailing newline (and carriage return, if present) */
3115 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3116 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3121 if (ipc->Buf == NULL)
3124 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3126 ipc->BufPtr = ipc->Buf;
3130 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3131 if (ipc->BufUsed == 0)
3132 ReadNetworkChunk(ipc);
3134 //// if (ipc->BufUsed != 0) while (1)
3140 aeptr = ipc->Buf + ipc->BufSize;
3141 while ((aptr < aeptr) &&
3145 *(bptr++) = *(aptr++);
3146 if ((*aptr == '\n') && (aptr < aeptr))
3148 /* Terminate it right, remove the line breaks */
3149 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3151 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3154 // 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);
3155 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3158 /* is there more in the buffer we need to read later? */
3159 if (ipc->Buf + ipc->BufUsed > aptr)
3166 ipc->BufPtr = ipc->Buf;
3168 // error_printf("----bla6\n");
3171 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3172 else if ((ipc->BufPtr != ipc->Buf) &&
3173 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3175 size_t NewBufSize = ipc->BufSize * 2;
3176 int delta = (ipc->BufPtr - ipc->Buf);
3179 /* if the line would end after our buffer, we should use a bigger buffer. */
3180 NewBuf = (char *)malloc (NewBufSize + 10);
3181 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3183 ipc->Buf = ipc->BufPtr = NewBuf;
3184 ipc->BufUsed -= delta;
3185 ipc->BufSize = NewBufSize;
3187 if (ReadNetworkChunk(ipc) <0)
3189 // error_printf("----bla\n");
3193 /// error_printf("----bl45761%s\nipc->BufUsed");
3195 // error_printf("----bla1\n");
3198 #else /* CHUNKED_READ */
3200 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3204 /* Read one character at a time. */
3206 serv_read(ipc, &buf[i], 1);
3207 if (buf[i] == '\n' || i == (SIZ-1))
3211 /* If we got a long line, discard characters until the newline. */
3213 while (buf[i] != '\n')
3214 serv_read(ipc, &buf[i], 1);
3216 /* Strip the trailing newline (and carriage return, if present) */
3217 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3218 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3222 #endif /* CHUNKED_READ */
3225 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3227 CtdlIPC_getline(ipc, buf);
3231 * send line to server - implemented in terms of serv_write()
3233 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3239 cmd = malloc(len + 2);
3241 /* This requires no extra memory */
3242 serv_write(ipc, buf, len);
3243 serv_write(ipc, "\n", 1);
3245 /* This is network-optimized */
3246 strncpy(cmd, buf, len);
3247 strcpy(cmd + len, "\n");
3248 serv_write(ipc, cmd, len + 1);
3252 ipc->last_command_sent = time(NULL);
3255 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3257 CtdlIPC_putline(ipc, buf);
3264 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3272 ipc = ialloc(CtdlIPC);
3276 #if defined(HAVE_OPENSSL)
3278 CtdlIPC_init_OpenSSL();
3280 #if defined(HAVE_PTHREAD_H)
3281 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3283 ipc->sock = -1; /* Not connected */
3284 ipc->isLocal = 0; /* Not local, of course! */
3285 ipc->downloading = 0;
3287 ipc->last_command_sent = 0L;
3288 ipc->network_status_cb = NULL;
3293 strcpy(cithost, DEFAULT_HOST); /* default host */
3294 strcpy(citport, DEFAULT_PORT); /* default port */
3296 /* Allow caller to supply our values (Windows) */
3297 if (hostbuf && strlen(hostbuf) > 0)
3298 strcpy(cithost, hostbuf);
3299 if (portbuf && strlen(portbuf) > 0)
3300 strcpy(citport, portbuf);
3302 /* Read host/port from command line if present */
3303 for (a = 0; a < argc; ++a) {
3306 } else if (a == 1) {
3307 strcpy(cithost, argv[a]);
3308 } else if (a == 2) {
3309 strcpy(citport, argv[a]);
3311 error_printf("%s: usage: ",argv[0]);
3312 error_printf("%s [host] [port] ",argv[0]);
3319 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3323 /* If we're using a unix domain socket we can do a bunch of stuff */
3324 if (!strcmp(cithost, UDS)) {
3325 if (!strcasecmp(citport, DEFAULT_PORT)) {
3326 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3329 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3331 printf("[%s]\n", sockpath);
3332 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3333 if (ipc->sock == -1) {
3337 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3338 if (portbuf != NULL) strcpy(portbuf, sockpath);
3339 strcpy(ipc->ip_hostname, "");
3340 strcpy(ipc->ip_address, "");
3344 printf("[%s:%s]\n", cithost, citport);
3345 ipc->sock = tcp_connectsock(cithost, citport);
3346 if (ipc->sock == -1) {
3352 /* Learn the actual network identity of the host to which we are connected */
3354 struct sockaddr_in6 clientaddr;
3355 unsigned int addrlen = sizeof(clientaddr);
3357 ipc->ip_hostname[0] = 0;
3358 ipc->ip_address[0] = 0;
3360 getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3361 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3362 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3364 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3365 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3368 /* stuff other things elsewhere */
3370 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3371 if (portbuf != NULL) strcpy(portbuf, citport);
3377 * Disconnect and delete the IPC class (destructor)
3379 void CtdlIPC_delete(CtdlIPC* ipc)
3383 SSL_shutdown(ipc->ssl);
3388 if (ipc->sock > -1) {
3389 shutdown(ipc->sock, 2); /* Close it up */
3392 if (ipc->Buf != NULL)
3401 * Disconnect and delete the IPC class (destructor)
3402 * Also NULLs out the pointer
3404 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3406 CtdlIPC_delete(*pipc);
3412 * return the file descriptor of the server socket so we can select() on it.
3414 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3417 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3424 * return one character
3426 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3429 char CtdlIPC_get(CtdlIPC* ipc)
3434 serv_read(ipc, buf, 1);