2 * Copyright (c) 1987-2016 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,
2051 char **listing, char *cret)
2057 if (!cret) return -2;
2058 if (!mimetype) return -2;
2059 if (!listing) return -2;
2060 if (*listing) return -2;
2062 aaa = malloc(strlen(mimetype) + 13);
2063 if (!aaa) return -1;
2064 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2065 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
2066 listing, &bytes, cret);
2073 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
2074 const char *listing, char *cret)
2079 if (!cret) return -2;
2080 if (!mimetype) return -2;
2081 if (!listing) return -2;
2083 aaa = malloc(strlen(mimetype) + 13);
2084 if (!aaa) return -1;
2085 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2086 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
2094 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
2098 if (!cret) return -2;
2099 if (!listing) return -2;
2100 if (*listing) return -2;
2102 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
2103 listing, &bytes, cret);
2108 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
2110 if (!cret) return -2;
2111 if (!listing) return -2;
2113 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
2119 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2123 if (!cret) return -2;
2124 if (session < 0) return -2;
2126 sprintf(aaa, "REQT %d", session);
2127 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2132 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2136 if (!cret) return -2;
2137 if (msgnum < 0) return -2;
2139 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2140 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2145 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2154 /* New SSL object */
2155 temp_ssl = SSL_new(ssl_ctx);
2157 error_printf("SSL_new failed: %s\n",
2158 ERR_reason_error_string(ERR_get_error()));
2161 /* Pointless flag waving */
2162 #if SSLEAY_VERSION_NUMBER >= 0x0922
2163 SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
2166 if (!access(EGD_POOL, F_OK))
2169 if (!RAND_status()) {
2170 error_printf("PRNG not properly seeded\n");
2174 /* Associate network connection with SSL object */
2175 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2176 error_printf("SSL_set_fd failed: %s\n",
2177 ERR_reason_error_string(ERR_get_error()));
2181 if (status_hook != NULL)
2182 status_hook("Requesting encryption...\r");
2184 /* Ready to start SSL/TLS */
2186 CtdlIPC_putline(ipc, "STLS");
2187 CtdlIPC_getline(ipc, buf);
2188 if (buf[0] != '2') {
2189 error_printf("Server can't start TLS: %s\n", buf);
2193 r = CtdlIPCGenericCommand(ipc,
2194 "STLS", NULL, 0, NULL, NULL, cret);
2196 error_printf("Server can't start TLS: %s\n", buf);
2201 /* Do SSL/TLS handshake */
2202 if ((a = SSL_connect(temp_ssl)) < 1) {
2203 error_printf("SSL_connect failed: %s\n",
2204 ERR_reason_error_string(ERR_get_error()));
2208 ipc->ssl = temp_ssl;
2210 if (BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE))
2214 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2215 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2216 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2217 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2223 #endif /* HAVE_OPENSSL */
2228 static void endtls(SSL *ssl)
2239 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2244 if (!address) return -2;
2245 if (!cret) return -2;
2247 aaa = (char *)malloc(strlen(address) + 6);
2248 if (!aaa) return -1;
2250 sprintf(aaa, "QDIR %s", address);
2251 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2258 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2262 if (!cret) return -2;
2263 sprintf(aaa, "IPGM %d", secret);
2264 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2270 /* ************************************************************************** */
2271 /* Stuff below this line is not for public consumption */
2272 /* ************************************************************************** */
2275 /* Read a listing from the server up to 000. Append to dest if it exists */
2276 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2285 length = strlen(ret);
2290 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2291 linelength = strlen(aaa);
2292 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2294 strcpy(&ret[length], aaa);
2295 length += linelength;
2296 strcpy(&ret[length++], "\n");
2304 /* Send a listing to the server; generate the ending 000. */
2305 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2309 text = (char *)malloc(strlen(listing) + 6);
2311 strcpy(text, listing);
2312 while (text[strlen(text) - 1] == '\n')
2313 text[strlen(text) - 1] = '\0';
2314 strcat(text, "\n000");
2315 CtdlIPC_putline(ipc, text);
2319 /* Malloc failed but we are committed to send */
2320 /* This may result in extra blanks at the bottom */
2321 CtdlIPC_putline(ipc, text);
2322 CtdlIPC_putline(ipc, "000");
2328 /* Partial read of file from server */
2329 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2331 register size_t len = 0;
2335 if (!cret) return 0;
2336 if (bytes < 1) return 0;
2339 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2340 CtdlIPC_putline(ipc, aaa);
2341 CtdlIPC_getline(ipc, aaa);
2343 strcpy(cret, &aaa[4]);
2345 len = extract_long(&aaa[4], 0);
2346 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2348 /* I know what I'm doing */
2349 serv_read(ipc, ((char *)(*buf) + offset), len);
2351 /* We have to read regardless */
2352 serv_read(ipc, aaa, len);
2356 CtdlIPC_unlock(ipc);
2362 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2366 if (!cret) return -2;
2367 if (!ipc->downloading) return -2;
2369 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2371 ipc->downloading = 0;
2377 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2381 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2382 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2389 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2390 void (*progress_gauge_callback)
2391 (CtdlIPC*, unsigned long, unsigned long),
2394 register size_t len;
2396 if (!cret) return -1;
2397 if (!buf) return -1;
2398 if (*buf) return -1;
2399 if (!ipc->downloading) return -1;
2402 if (progress_gauge_callback)
2403 progress_gauge_callback(ipc, len, bytes);
2404 while (len < bytes) {
2405 register size_t block;
2407 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2413 if (progress_gauge_callback)
2414 progress_gauge_callback(ipc, len, bytes);
2419 /* READ - pipelined */
2420 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2422 void (*progress_gauge_callback)
2423 (CtdlIPC*, unsigned long, unsigned long),
2426 register size_t len;
2427 register int calls; /* How many calls in the pipeline */
2428 register int i; /* iterator */
2431 if (!cret) return -1;
2432 if (!buf) return -1;
2433 if (*buf) return -1;
2434 if (!ipc->downloading) return -1;
2436 *buf = (void *)realloc(*buf, bytes - resume);
2437 if (!*buf) return -1;
2441 if (progress_gauge_callback)
2442 progress_gauge_callback(ipc, len, bytes);
2444 /* How many calls will be in the pipeline? */
2445 calls = (bytes - resume) / 4096;
2446 if ((bytes - resume) % 4096) calls++;
2448 /* Send all requests at once */
2449 for (i = 0; i < calls; i++) {
2450 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2451 CtdlIPC_putline(ipc, aaa);
2454 /* Receive all responses at once */
2455 for (i = 0; i < calls; i++) {
2456 CtdlIPC_getline(ipc, aaa);
2458 strcpy(cret, &aaa[4]);
2460 len = extract_long(&aaa[4], 0);
2461 /* I know what I'm doing */
2462 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2464 if (progress_gauge_callback)
2465 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2467 CtdlIPC_unlock(ipc);
2473 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2478 if (!cret) return -1;
2479 if (!ipc->uploading) return -1;
2481 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2482 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2489 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2490 void (*progress_gauge_callback)
2491 (CtdlIPC*, unsigned long, unsigned long),
2494 register int ret = -1;
2495 register size_t offset = 0;
2499 FILE *fd = uploadFP;
2502 if (!cret) return -1;
2504 fseek(fd, 0L, SEEK_END);
2508 if (progress_gauge_callback)
2509 progress_gauge_callback(ipc, 0, bytes);
2511 while (offset < bytes) {
2512 register size_t to_write;
2514 /* Read some data in */
2515 to_write = fread(buf, 1, 4096, fd);
2517 if (feof(fd) || ferror(fd)) break;
2519 sprintf(aaa, "WRIT %d", (int)to_write);
2520 CtdlIPC_putline(ipc, aaa);
2521 CtdlIPC_getline(ipc, aaa);
2522 strcpy(cret, &aaa[4]);
2524 if (aaa[0] == '7') {
2525 to_write = extract_long(&aaa[4], 0);
2527 serv_write(ipc, buf, to_write);
2529 if (progress_gauge_callback)
2530 progress_gauge_callback(ipc, offset, bytes);
2531 /* Detect short reads and back up if needed */
2532 /* offset will never be negative anyway */
2533 fseek(fd, (signed)offset, SEEK_SET);
2538 if (progress_gauge_callback)
2539 progress_gauge_callback(ipc, 1, 1);
2542 return (!ferr ? ret : -2);
2547 * Generic command method. This method should handle any server command
2548 * except for CHAT. It takes the following arguments:
2550 * ipc The server to speak with
2551 * command Preformatted command to send to server
2552 * to_send A text or binary file to send to server
2553 * (only sent if server requests it)
2554 * bytes_to_send The number of bytes in to_send (required if
2555 * sending binary, optional if sending listing)
2556 * to_receive Pointer to a NULL pointer, if the server
2557 * sends text or binary we will allocate memory
2558 * for the file and stuff it here
2559 * bytes_to_receive If a file is received, we will store its
2561 * proto_response The protocol response. Caller must provide
2562 * this buffer and ensure that it is at least
2563 * 128 bytes in length.
2565 * This function returns a number equal to the protocol response number,
2566 * -1 if an internal error occurred, -2 if caller provided bad values,
2567 * or 0 - the protocol response number if bad values were found during
2568 * the protocol exchange.
2569 * It stores the protocol response string (minus the number) in
2570 * protocol_response as described above. Some commands send additional
2571 * data in this string.
2573 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2574 const char *command, const char *to_send,
2575 size_t bytes_to_send, char **to_receive,
2576 size_t *bytes_to_receive, char *proto_response)
2581 if (!command) return -2;
2582 if (!proto_response) return -2;
2585 CtdlIPC_putline(ipc, command);
2587 CtdlIPC_getline(ipc, proto_response);
2588 if (proto_response[3] == '*')
2590 ret = atoi(proto_response);
2591 strcpy(proto_response, &proto_response[4]);
2592 switch (ret / 100) {
2593 default: /* Unknown, punt */
2595 case 3: /* MORE_DATA */
2597 /* Don't need to do anything */
2599 case 1: /* LISTING_FOLLOWS */
2600 if (to_receive && !*to_receive && bytes_to_receive) {
2601 *to_receive = CtdlIPCReadListing(ipc, NULL);
2602 } else { /* Drain */
2603 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2607 case 4: /* SEND_LISTING */
2609 CtdlIPCSendListing(ipc, to_send);
2611 /* No listing given, fake it */
2612 CtdlIPC_putline(ipc, "000");
2616 case 6: /* BINARY_FOLLOWS */
2617 if (to_receive && !*to_receive && bytes_to_receive) {
2619 extract_long(proto_response, 0);
2620 *to_receive = (char *)
2621 malloc((size_t)*bytes_to_receive);
2625 serv_read(ipc, *to_receive,
2632 drain = extract_long(proto_response, 0);
2633 while (drain > SIZ) {
2634 serv_read(ipc, buf, SIZ);
2637 serv_read(ipc, buf, drain);
2641 case 7: /* SEND_BINARY */
2642 if (to_send && bytes_to_send) {
2643 serv_write(ipc, to_send, bytes_to_send);
2644 } else if (bytes_to_send) {
2645 /* Fake it, send nulls */
2648 fake = bytes_to_send;
2649 memset(buf, '\0', SIZ);
2650 while (fake > SIZ) {
2651 serv_write(ipc, buf, SIZ);
2654 serv_write(ipc, buf, fake);
2656 } /* else who knows? DANGER WILL ROBINSON */
2658 case 8: /* START_CHAT_MODE */
2659 if (!strncasecmp(command, "CHAT", 4)) {
2660 /* Don't call chatmode with generic! */
2661 CtdlIPC_putline(ipc, "/quit");
2664 /* In this mode we send then receive listing */
2666 CtdlIPCSendListing(ipc, to_send);
2668 /* No listing given, fake it */
2669 CtdlIPC_putline(ipc, "000");
2672 if (to_receive && !*to_receive
2673 && bytes_to_receive) {
2674 *to_receive = CtdlIPCReadListing(ipc, NULL);
2675 } else { /* Drain */
2676 while (CtdlIPC_getline(ipc, buf),
2677 strcmp(buf, "000")) ;
2682 case 9: /* ASYNC_MSG */
2683 /* CtdlIPCDoAsync(ret, proto_response); */
2684 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2690 CtdlIPC_unlock(ipc);
2696 * Connect to a Citadel on a remote host using a TCP/IP socket
2698 static int tcp_connectsock(char *host, char *service)
2700 struct in6_addr serveraddr;
2701 struct addrinfo hints;
2702 struct addrinfo *res = NULL;
2703 struct addrinfo *ai = NULL;
2707 if ((host == NULL) || IsEmptyStr(host)) {
2708 service = DEFAULT_HOST ;
2710 if ((service == NULL) || IsEmptyStr(service)) {
2711 service = DEFAULT_PORT ;
2714 memset(&hints, 0x00, sizeof(hints));
2715 hints.ai_flags = AI_NUMERICSERV;
2716 hints.ai_family = AF_UNSPEC;
2717 hints.ai_socktype = SOCK_STREAM;
2720 * Handle numeric IPv4 and IPv6 addresses
2722 rc = inet_pton(AF_INET, host, &serveraddr);
2723 if (rc == 1) { /* dotted quad */
2724 hints.ai_family = AF_INET;
2725 hints.ai_flags |= AI_NUMERICHOST;
2728 rc = inet_pton(AF_INET6, host, &serveraddr);
2729 if (rc == 1) { /* IPv6 address */
2730 hints.ai_family = AF_INET6;
2731 hints.ai_flags |= AI_NUMERICHOST;
2735 /* Begin the connection process */
2737 rc = getaddrinfo(host, service, &hints, &res);
2743 * Try all available addresses until we connect to one or until we run out.
2745 for (ai = res; ai != NULL; ai = ai->ai_next) {
2746 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2747 if (sock < 0) return(-1);
2749 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2751 return(sock); /* Connected! */
2754 close(sock); /* Failed. Close the socket to avoid fd leak! */
2766 * Connect to a Citadel on the local host using a unix domain socket
2768 static int uds_connectsock(int *isLocal, char *sockpath)
2770 struct sockaddr_un addr;
2773 memset(&addr, 0, sizeof(addr));
2774 addr.sun_family = AF_UNIX;
2775 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2777 s = socket(AF_UNIX, SOCK_STREAM, 0);
2782 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2793 * input binary data from socket
2795 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2797 unsigned int len, rlen;
2799 #if defined(HAVE_OPENSSL)
2801 serv_read_ssl(ipc, buf, bytes);
2806 while (len < bytes) {
2807 rlen = read(ipc->sock, &buf[len], bytes - len);
2809 connection_died(ipc, 0);
2818 * send binary to server
2820 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2822 unsigned int bytes_written = 0;
2825 #if defined(HAVE_OPENSSL)
2827 serv_write_ssl(ipc, buf, nbytes);
2831 while (bytes_written < nbytes) {
2832 retval = write(ipc->sock, &buf[bytes_written],
2833 nbytes - bytes_written);
2835 connection_died(ipc, 0);
2838 bytes_written += retval;
2845 * input binary data from encrypted connection
2847 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2853 while (len < bytes) {
2854 if (SSL_want_read(ipc->ssl)) {
2855 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2856 error_printf("SSL_write in serv_read:\n");
2857 ERR_print_errors_fp(stderr);
2860 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2864 errval = SSL_get_error(ipc->ssl, rlen);
2865 if (errval == SSL_ERROR_WANT_READ ||
2866 errval == SSL_ERROR_WANT_WRITE) {
2871 Not sure why we'd want to handle these error codes any differently,
2872 but this definitely isn't the way to handle them. Someone must have
2873 naively assumed that we could fall back to unencrypted communications,
2874 but all it does is just recursively blow the stack.
2875 if (errval == SSL_ERROR_ZERO_RETURN ||
2876 errval == SSL_ERROR_SSL) {
2877 serv_read(ipc, &buf[len], bytes - len);
2881 error_printf("SSL_read in serv_read: %s\n",
2882 ERR_reason_error_string(ERR_peek_error()));
2883 connection_died(ipc, 1);
2892 * send binary to server encrypted
2894 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2896 unsigned int bytes_written = 0;
2900 while (bytes_written < nbytes) {
2901 if (SSL_want_write(ipc->ssl)) {
2902 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2903 error_printf("SSL_read in serv_write:\n");
2904 ERR_print_errors_fp(stderr);
2907 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2908 nbytes - bytes_written);
2912 errval = SSL_get_error(ipc->ssl, retval);
2913 if (errval == SSL_ERROR_WANT_READ ||
2914 errval == SSL_ERROR_WANT_WRITE) {
2918 if (errval == SSL_ERROR_ZERO_RETURN ||
2919 errval == SSL_ERROR_SSL) {
2920 serv_write(ipc, &buf[bytes_written],
2921 nbytes - bytes_written);
2924 error_printf("SSL_write in serv_write: %s\n",
2925 ERR_reason_error_string(ERR_peek_error()));
2926 connection_died(ipc, 1);
2929 bytes_written += retval;
2934 #ifdef THREADED_CLIENT
2935 static void ssl_lock(int mode, int n, const char *file, int line)
2937 if (mode & CRYPTO_LOCK)
2938 pthread_mutex_lock(Critters[n]);
2940 pthread_mutex_unlock(Critters[n]);
2942 #endif /* THREADED_CLIENT */
2945 static void CtdlIPC_init_OpenSSL(void)
2948 const SSL_METHOD *ssl_method;
2951 /* already done init */
2960 SSL_load_error_strings();
2961 SSLeay_add_ssl_algorithms();
2963 /* Set up the SSL context in which we will oeprate */
2964 ssl_method = SSLv23_client_method();
2965 ssl_ctx = SSL_CTX_new(ssl_method);
2967 error_printf("SSL_CTX_new failed: %s\n",
2968 ERR_reason_error_string(ERR_get_error()));
2971 /* Any reasonable cipher we can get */
2972 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2973 error_printf("No ciphers available for encryption\n");
2976 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2978 /* Load DH parameters into the context */
2981 error_printf("Can't allocate a DH object: %s\n",
2982 ERR_reason_error_string(ERR_get_error()));
2985 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2986 error_printf("Can't assign DH_P: %s\n",
2987 ERR_reason_error_string(ERR_get_error()));
2991 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2992 error_printf("Can't assign DH_G: %s\n",
2993 ERR_reason_error_string(ERR_get_error()));
2998 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
3001 #ifdef THREADED_CLIENT
3002 /* OpenSSL requires callbacks for threaded clients */
3003 CRYPTO_set_locking_callback(ssl_lock);
3004 CRYPTO_set_id_callback(id_callback);
3006 /* OpenSSL requires us to do semaphores for threaded clients */
3007 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
3009 perror("malloc failed");
3012 for (a = 0; a < CRYPTO_num_locks(); a++) {
3013 Critters[a] = malloc(sizeof (pthread_mutex_t));
3015 perror("malloc failed");
3018 pthread_mutex_init(Critters[a], NULL);
3021 #endif /* THREADED_CLIENT */
3026 #ifdef THREADED_CLIENT
3027 static unsigned long id_callback(void) {
3028 return (unsigned long)pthread_self();
3030 #endif /* THREADED_CLIENT */
3031 #endif /* HAVE_OPENSSL */
3035 ReadNetworkChunk(CtdlIPC* ipc)
3052 FD_SET(ipc->sock, &read_fd);
3053 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
3055 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
3059 *(ipc->BufPtr) = '\0';
3060 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3061 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3063 ipc->BufPtr[n]='\0';
3071 if (!(errno == EINTR || errno == EAGAIN))
3072 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
3078 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3080 ipc->BufPtr[n]='\0';
3085 connection_died(ipc, 0);
3093 * input string from socket - implemented in terms of serv_read()
3097 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3100 char *aptr, *bptr, *aeptr, *beptr;
3102 // error_printf("---\n");
3105 #if defined(HAVE_OPENSSL)
3108 /* Read one character at a time. */
3110 serv_read(ipc, &buf[i], 1);
3111 if (buf[i] == '\n' || i == (SIZ-1))
3115 /* If we got a long line, discard characters until the newline. */
3117 while (buf[i] != '\n')
3118 serv_read(ipc, &buf[i], 1);
3120 /* Strip the trailing newline (and carriage return, if present) */
3121 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3122 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3127 if (ipc->Buf == NULL)
3130 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3132 ipc->BufPtr = ipc->Buf;
3136 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3137 if (ipc->BufUsed == 0)
3138 ReadNetworkChunk(ipc);
3140 //// if (ipc->BufUsed != 0) while (1)
3146 aeptr = ipc->Buf + ipc->BufSize;
3147 while ((aptr < aeptr) &&
3151 *(bptr++) = *(aptr++);
3152 if ((*aptr == '\n') && (aptr < aeptr))
3154 /* Terminate it right, remove the line breaks */
3155 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3157 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3160 // 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);
3161 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3164 /* is there more in the buffer we need to read later? */
3165 if (ipc->Buf + ipc->BufUsed > aptr)
3172 ipc->BufPtr = ipc->Buf;
3174 // error_printf("----bla6\n");
3177 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3178 else if ((ipc->BufPtr != ipc->Buf) &&
3179 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3181 size_t NewBufSize = ipc->BufSize * 2;
3182 int delta = (ipc->BufPtr - ipc->Buf);
3185 /* if the line would end after our buffer, we should use a bigger buffer. */
3186 NewBuf = (char *)malloc (NewBufSize + 10);
3187 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3189 ipc->Buf = ipc->BufPtr = NewBuf;
3190 ipc->BufUsed -= delta;
3191 ipc->BufSize = NewBufSize;
3193 if (ReadNetworkChunk(ipc) <0)
3195 // error_printf("----bla\n");
3199 /// error_printf("----bl45761%s\nipc->BufUsed");
3201 // error_printf("----bla1\n");
3204 #else /* CHUNKED_READ */
3206 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3210 /* Read one character at a time. */
3212 serv_read(ipc, &buf[i], 1);
3213 if (buf[i] == '\n' || i == (SIZ-1))
3217 /* If we got a long line, discard characters until the newline. */
3219 while (buf[i] != '\n')
3220 serv_read(ipc, &buf[i], 1);
3222 /* Strip the trailing newline (and carriage return, if present) */
3223 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3224 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3228 #endif /* CHUNKED_READ */
3231 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3233 CtdlIPC_getline(ipc, buf);
3237 * send line to server - implemented in terms of serv_write()
3239 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3245 cmd = malloc(len + 2);
3247 /* This requires no extra memory */
3248 serv_write(ipc, buf, len);
3249 serv_write(ipc, "\n", 1);
3251 /* This is network-optimized */
3252 strncpy(cmd, buf, len);
3253 strcpy(cmd + len, "\n");
3254 serv_write(ipc, cmd, len + 1);
3258 ipc->last_command_sent = time(NULL);
3261 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3263 CtdlIPC_putline(ipc, buf);
3270 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3278 ipc = ialloc(CtdlIPC);
3282 #if defined(HAVE_OPENSSL)
3284 CtdlIPC_init_OpenSSL();
3286 #if defined(HAVE_PTHREAD_H)
3287 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3289 ipc->sock = -1; /* Not connected */
3290 ipc->isLocal = 0; /* Not local, of course! */
3291 ipc->downloading = 0;
3293 ipc->last_command_sent = 0L;
3294 ipc->network_status_cb = NULL;
3299 strcpy(cithost, DEFAULT_HOST); /* default host */
3300 strcpy(citport, DEFAULT_PORT); /* default port */
3302 /* Allow caller to supply our values (Windows) */
3303 if (hostbuf && strlen(hostbuf) > 0)
3304 strcpy(cithost, hostbuf);
3305 if (portbuf && strlen(portbuf) > 0)
3306 strcpy(citport, portbuf);
3308 /* Read host/port from command line if present */
3309 for (a = 0; a < argc; ++a) {
3312 } else if (a == 1) {
3313 strcpy(cithost, argv[a]);
3314 } else if (a == 2) {
3315 strcpy(citport, argv[a]);
3317 error_printf("%s: usage: ",argv[0]);
3318 error_printf("%s [host] [port] ",argv[0]);
3325 if ((!strcmp(cithost, "localhost"))
3326 || (!strcmp(cithost, "127.0.0.1"))) {
3330 /* If we're using a unix domain socket we can do a bunch of stuff */
3331 if (!strcmp(cithost, UDS)) {
3332 if (!strcasecmp(citport, DEFAULT_PORT)) {
3333 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3336 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3338 printf("[%s]\n", sockpath);
3339 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3340 if (ipc->sock == -1) {
3344 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3345 if (portbuf != NULL) strcpy(portbuf, sockpath);
3346 strcpy(ipc->ip_hostname, "");
3347 strcpy(ipc->ip_address, "");
3351 printf("[%s:%s]\n", cithost, citport);
3352 ipc->sock = tcp_connectsock(cithost, citport);
3353 if (ipc->sock == -1) {
3359 /* Learn the actual network identity of the host to which we are connected */
3361 struct sockaddr_in6 clientaddr;
3362 unsigned int addrlen = sizeof(clientaddr);
3364 ipc->ip_hostname[0] = 0;
3365 ipc->ip_address[0] = 0;
3367 getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3368 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3369 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3371 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3372 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3375 /* stuff other things elsewhere */
3377 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3378 if (portbuf != NULL) strcpy(portbuf, citport);
3384 * Disconnect and delete the IPC class (destructor)
3386 void CtdlIPC_delete(CtdlIPC* ipc)
3390 SSL_shutdown(ipc->ssl);
3395 if (ipc->sock > -1) {
3396 shutdown(ipc->sock, 2); /* Close it up */
3399 if (ipc->Buf != NULL)
3408 * Disconnect and delete the IPC class (destructor)
3409 * Also NULLs out the pointer
3411 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3413 CtdlIPC_delete(*pipc);
3419 * return the file descriptor of the server socket so we can select() on it.
3421 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3424 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3431 * return one character
3433 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3436 char CtdlIPC_get(CtdlIPC* ipc)
3441 serv_read(ipc, buf, 1);