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 int ret = 221; /* Default to successful quit */
257 if (ipc->sock > -1) {
258 CtdlIPC_putline(ipc, "QUIT");
259 CtdlIPC_getline(ipc, aaa);
264 SSL_shutdown(ipc->ssl);
268 shutdown(ipc->sock, 2); /* Close connection; we're dead */
276 * Asks the server to log out. Should always return 200, even if no user
277 * was logged in. The user will not be logged in after this!
279 int CtdlIPCLogout(CtdlIPC *ipc)
285 CtdlIPC_putline(ipc, "LOUT");
286 CtdlIPC_getline(ipc, aaa);
294 * First stage of authentication - pass the username. Returns 300 if the
295 * username is able to log in, with the username correctly spelled in cret.
296 * Returns various 500 error codes if the user doesn't exist, etc.
298 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
303 if (!username) return -2;
304 if (!cret) return -2;
306 aaa = (char *)malloc((size_t)(strlen(username) + 6));
309 sprintf(aaa, "USER %s", username);
310 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
317 * Second stage of authentication - provide password. The server returns
318 * 200 and several arguments in cret relating to the user's account.
320 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
325 if (!passwd) return -2;
326 if (!cret) return -2;
328 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
331 sprintf(aaa, "PASS %s", passwd);
332 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
339 * Second stage of authentication - provide password. The server returns
340 * 200 and several arguments in cret relating to the user's account.
342 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
347 if (!response) return -2;
348 if (!cret) return -2;
350 aaa = (char *)malloc((size_t)(strlen(response) + 6));
353 sprintf(aaa, "PAS2 %s", response);
354 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
361 * Create a new user. This returns 200 plus the same arguments as TryPassword
362 * if selfservice is nonzero, unless there was a problem creating the account.
363 * If selfservice is zero, creates a new user but does not log out the existing
364 * user - intended for use by system administrators to create accounts on
365 * behalf of other users.
367 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
372 if (!username) return -2;
373 if (!cret) return -2;
375 aaa = (char *)malloc((size_t)(strlen(username) + 6));
378 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
379 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
386 * Changes the user's password. Returns 200 if changed, errors otherwise.
388 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
393 if (!passwd) return -2;
394 if (!cret) return -2;
396 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
399 sprintf(aaa, "SETP %s", passwd);
400 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
407 /* Caller must free the march list */
408 /* Room types are defined in enum RoomList; keep these in sync! */
409 /* floor is -1 for all, or floornum */
410 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
413 struct march *march = NULL;
414 static char *proto[] =
415 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
420 if (!listing) return -2;
421 if (*listing) return -2; /* Free the listing first */
422 if (!cret) return -2;
423 /* if (which < 0 || which > 4) return -2; */
424 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
426 sprintf(aaa, "%s %d", proto[which], floor);
427 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
428 if (ret / 100 == 1) {
431 while (bbb && strlen(bbb)) {
434 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
436 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
437 mptr = (struct march *) malloc(sizeof (struct march));
440 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
441 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
442 mptr->march_floor = (char) extract_int(aaa, 2);
443 mptr->march_order = (char) extract_int(aaa, 3);
444 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
445 mptr->march_access = (char) extract_int(aaa, 5);
452 while (mptr2->next != NULL)
466 /* Caller must free the struct ctdluser; caller may pass an existing one */
467 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
471 if (!cret) return -2;
472 if (!uret) return -2;
473 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
474 if (!*uret) return -1;
476 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
477 if (ret / 100 == 2) {
478 uret[0]->flags = extract_int(cret, 2);
485 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
489 if (!uret) return -2;
490 if (!cret) return -2;
496 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
501 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
506 if (!oldname) return -2;
507 if (!newname) return -2;
508 if (!cret) return -2;
510 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
511 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
517 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
518 struct ctdlipcroom **rret, char *cret)
523 if (!cret) return -2;
524 if (!rret) return -2;
525 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
526 if (!*rret) return -1;
529 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
534 sprintf(aaa, "GOTO %s|%s", room, passwd);
536 aaa = (char *)malloc(strlen(room) + 6);
541 sprintf(aaa, "GOTO %s", room);
543 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
544 if (ret / 100 == 2) {
545 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
546 rret[0]->RRunread = extract_long(cret, 1);
547 rret[0]->RRtotal = extract_long(cret, 2);
548 rret[0]->RRinfoupdated = extract_int(cret, 3);
549 rret[0]->RRflags = extract_int(cret, 4);
550 rret[0]->RRhighest = extract_long(cret, 5);
551 rret[0]->RRlastread = extract_long(cret, 6);
552 rret[0]->RRismailbox = extract_int(cret, 7);
553 rret[0]->RRaide = extract_int(cret, 8);
554 rret[0]->RRnewmail = extract_long(cret, 9);
555 rret[0]->RRfloor = extract_int(cret, 10);
556 rret[0]->RRcurrentview = extract_int(cret, 11);
557 rret[0]->RRdefaultview = extract_int(cret, 12);
558 /* position 13 is a trash folder flag ... irrelevant in this client */
559 rret[0]->RRflags2 = extract_int(cret, 14);
570 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
571 /* whicharg is number of messages, applies to last, first, gt, lt */
572 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
573 const char *mtemplate, unsigned long **mret, char *cret)
576 unsigned long count = 0;
577 static char *proto[] =
578 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
583 if (!cret) return -2;
584 if (!mret) return -2;
585 if (*mret) return -2;
586 if (which < 0 || which > 6) return -2;
589 sprintf(aaa, "MSGS %s||%d", proto[which],
590 (mtemplate) ? 1 : 0);
592 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
593 (mtemplate) ? 1 : 0);
594 if (mtemplate) count = strlen(mtemplate);
595 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
599 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
602 while (bbb && strlen(bbb)) {
603 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
604 remove_token(bbb, 0, '\n');
605 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
606 sizeof (unsigned long)));
608 (*mret)[count++] = atol(aaa);
620 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
621 struct ctdlipcmessage **mret, char *cret)
627 int multipart_hunting = 0;
628 char multipart_prefix[128];
631 if (!cret) return -1;
632 if (!mret) return -1;
633 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
634 if (!*mret) return -1;
635 if (!msgnum) return -1;
637 strcpy(encoding, "");
638 strcpy(mret[0]->content_type, "");
639 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
640 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
641 if (ret / 100 == 1) {
643 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
644 while (strlen(bbb) > 4 && bbb[4] == '=') {
645 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
646 remove_token(bbb, 0, '\n');
648 if (!strncasecmp(aaa, "nhdr=yes", 8))
650 else if (!strncasecmp(aaa, "from=", 5))
651 safestrncpy(mret[0]->author, &aaa[5], SIZ);
652 else if (!strncasecmp(aaa, "type=", 5))
653 mret[0]->type = atoi(&aaa[5]);
654 else if (!strncasecmp(aaa, "msgn=", 5))
655 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
656 else if (!strncasecmp(aaa, "subj=", 5))
657 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
658 else if (!strncasecmp(aaa, "rfca=", 5))
659 safestrncpy(mret[0]->email, &aaa[5], SIZ);
660 else if (!strncasecmp(aaa, "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)
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, struct ctdluser **uret, char *cret)
1926 if (!cret) return -2;
1927 if (!uret) return -2;
1928 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1929 if (!*uret) return -1;
1931 sprintf(aaa, "AGUP %s", who);
1932 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1934 if (ret / 100 == 2) {
1935 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1936 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1937 uret[0]->flags = extract_int(cret, 2);
1938 uret[0]->timescalled = extract_long(cret, 3);
1939 uret[0]->posted = extract_long(cret, 4);
1940 uret[0]->axlevel = extract_int(cret, 5);
1941 uret[0]->usernum = extract_long(cret, 6);
1942 uret[0]->lastcall = extract_long(cret, 7);
1943 uret[0]->USuserpurge = extract_int(cret, 8);
1950 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1955 if (!cret) return -2;
1956 if (!uret) return -2;
1958 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1959 if (!aaa) return -1;
1961 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1962 uret->fullname, uret->password, uret->flags, uret->timescalled,
1963 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge
1965 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1972 int CtdlIPCAideGetEmailAddresses(CtdlIPC *ipc, const char *who, char *target_buf, char *cret)
1976 char *emailaddrs = NULL;
1977 size_t emailaddrs_len = 0;
1979 sprintf(aaa, "AGEA %s", who);
1980 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
1982 if (ret / 100 == 1) {
1983 strcpy(target_buf, emailaddrs);
1986 if (emailaddrs != NULL) {
1995 int CtdlIPCAideSetEmailAddresses(CtdlIPC *ipc, const char *who, char *emailaddrs, char *cret)
2000 if (!who) return -2;
2001 if (!emailaddrs) return -2;
2002 if (!cret) return -2;
2004 sprintf(aaa, "ASEA %s", who);
2005 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2011 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2012 /* caller must free the struct ExpirePolicy */
2013 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
2014 struct ExpirePolicy **policy, char *cret)
2016 static char *proto[] = {
2020 strof(mailboxespolicy)
2025 if (!cret) return -2;
2026 if (!policy) return -2;
2027 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
2028 if (!*policy) return -1;
2029 if (which < 0 || which > 3) return -2;
2031 sprintf(cmd, "GPEX %s", proto[which]);
2032 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2033 if (ret / 100 == 2) {
2034 policy[0]->expire_mode = extract_int(cret, 0);
2035 policy[0]->expire_value = extract_int(cret, 1);
2042 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2043 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2044 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
2045 struct ExpirePolicy *policy, char *cret)
2048 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2050 if (!cret) return -2;
2051 if (which < 0 || which > 3) return -2;
2052 if (!policy) return -2;
2053 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
2054 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
2056 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
2057 policy->expire_mode, policy->expire_value);
2058 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2063 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
2067 if (!cret) return -2;
2068 if (!listing) return -2;
2069 if (*listing) return -2;
2071 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
2072 listing, &bytes, cret);
2077 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
2079 if (!cret) return -2;
2080 if (!listing) return -2;
2082 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
2088 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype, char **listing, char *cret)
2094 if (!cret) return -2;
2095 if (!mimetype) return -2;
2096 if (!listing) return -2;
2097 if (*listing) return -2;
2099 aaa = malloc(strlen(mimetype) + 13);
2100 if (!aaa) return -1;
2101 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2102 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2109 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype, const char *listing, char *cret)
2114 if (!cret) return -2;
2115 if (!mimetype) return -2;
2116 if (!listing) return -2;
2118 aaa = malloc(strlen(mimetype) + 13);
2119 if (!aaa) return -1;
2120 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2121 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2128 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
2132 if (!cret) return -2;
2133 if (!listing) return -2;
2134 if (*listing) return -2;
2136 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2141 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
2143 if (!cret) return -2;
2144 if (!listing) return -2;
2146 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2151 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2155 if (!cret) return -2;
2156 if (session < 0) return -2;
2158 sprintf(aaa, "REQT %d", session);
2159 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2164 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2168 if (!cret) return -2;
2169 if (msgnum < 0) return -2;
2171 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2172 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2177 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2186 /* New SSL object */
2187 temp_ssl = SSL_new(ssl_ctx);
2189 error_printf("SSL_new failed: %s\n",
2190 ERR_reason_error_string(ERR_get_error()));
2193 /* Pointless flag waving */
2194 #if SSLEAY_VERSION_NUMBER >= 0x0922
2195 SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
2198 if (!access(EGD_POOL, F_OK))
2201 if (!RAND_status()) {
2202 error_printf("PRNG not properly seeded\n");
2206 /* Associate network connection with SSL object */
2207 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2208 error_printf("SSL_set_fd failed: %s\n",
2209 ERR_reason_error_string(ERR_get_error()));
2213 if (status_hook != NULL)
2214 status_hook("Requesting encryption...\r");
2216 /* Ready to start SSL/TLS */
2218 CtdlIPC_putline(ipc, "STLS");
2219 CtdlIPC_getline(ipc, buf);
2220 if (buf[0] != '2') {
2221 error_printf("Server can't start TLS: %s\n", buf);
2225 r = CtdlIPCGenericCommand(ipc,
2226 "STLS", NULL, 0, NULL, NULL, cret);
2228 error_printf("Server can't start TLS: %s\n", buf);
2233 /* Do SSL/TLS handshake */
2234 if ((a = SSL_connect(temp_ssl)) < 1) {
2235 error_printf("SSL_connect failed: %s\n",
2236 ERR_reason_error_string(ERR_get_error()));
2240 ipc->ssl = temp_ssl;
2242 if (BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE))
2246 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2247 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2248 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2249 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2255 #endif /* HAVE_OPENSSL */
2260 static void endtls(SSL *ssl)
2271 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2276 if (!address) return -2;
2277 if (!cret) return -2;
2279 aaa = (char *)malloc(strlen(address) + 6);
2280 if (!aaa) return -1;
2282 sprintf(aaa, "QDIR %s", address);
2283 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2290 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2294 if (!cret) return -2;
2295 sprintf(aaa, "IPGM %d", secret);
2296 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2302 /* ************************************************************************** */
2303 /* Stuff below this line is not for public consumption */
2304 /* ************************************************************************** */
2307 /* Read a listing from the server up to 000. Append to dest if it exists */
2308 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2317 length = strlen(ret);
2322 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2323 linelength = strlen(aaa);
2324 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2326 strcpy(&ret[length], aaa);
2327 length += linelength;
2328 strcpy(&ret[length++], "\n");
2336 /* Send a listing to the server; generate the ending 000. */
2337 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2341 text = (char *)malloc(strlen(listing) + 6);
2343 strcpy(text, listing);
2344 while (text[strlen(text) - 1] == '\n')
2345 text[strlen(text) - 1] = '\0';
2346 strcat(text, "\n000");
2347 CtdlIPC_putline(ipc, text);
2351 /* Malloc failed but we are committed to send */
2352 /* This may result in extra blanks at the bottom */
2353 CtdlIPC_putline(ipc, text);
2354 CtdlIPC_putline(ipc, "000");
2360 /* Partial read of file from server */
2361 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2367 if (!cret) return 0;
2368 if (bytes < 1) return 0;
2371 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2372 CtdlIPC_putline(ipc, aaa);
2373 CtdlIPC_getline(ipc, aaa);
2375 strcpy(cret, &aaa[4]);
2377 len = extract_long(&aaa[4], 0);
2378 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2380 /* I know what I'm doing */
2381 serv_read(ipc, ((char *)(*buf) + offset), len);
2383 /* We have to read regardless */
2384 serv_read(ipc, aaa, len);
2388 CtdlIPC_unlock(ipc);
2394 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2398 if (!cret) return -2;
2399 if (!ipc->downloading) return -2;
2401 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2403 ipc->downloading = 0;
2409 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2413 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2414 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2421 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2422 void (*progress_gauge_callback)
2423 (CtdlIPC*, unsigned long, unsigned long),
2428 if (!cret) return -1;
2429 if (!buf) return -1;
2430 if (*buf) return -1;
2431 if (!ipc->downloading) return -1;
2434 if (progress_gauge_callback)
2435 progress_gauge_callback(ipc, len, bytes);
2436 while (len < bytes) {
2439 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2445 if (progress_gauge_callback)
2446 progress_gauge_callback(ipc, len, bytes);
2451 /* READ - pipelined */
2452 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2454 void (*progress_gauge_callback)
2455 (CtdlIPC*, unsigned long, unsigned long),
2459 int calls; /* How many calls in the pipeline */
2460 int i; /* iterator */
2463 if (!cret) return -1;
2464 if (!buf) return -1;
2465 if (*buf) return -1;
2466 if (!ipc->downloading) return -1;
2468 *buf = (void *)realloc(*buf, bytes - resume);
2469 if (!*buf) return -1;
2473 if (progress_gauge_callback)
2474 progress_gauge_callback(ipc, len, bytes);
2476 /* How many calls will be in the pipeline? */
2477 calls = (bytes - resume) / 4096;
2478 if ((bytes - resume) % 4096) calls++;
2480 /* Send all requests at once */
2481 for (i = 0; i < calls; i++) {
2482 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2483 CtdlIPC_putline(ipc, aaa);
2486 /* Receive all responses at once */
2487 for (i = 0; i < calls; i++) {
2488 CtdlIPC_getline(ipc, aaa);
2490 strcpy(cret, &aaa[4]);
2492 len = extract_long(&aaa[4], 0);
2493 /* I know what I'm doing */
2494 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2496 if (progress_gauge_callback)
2497 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2499 CtdlIPC_unlock(ipc);
2505 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2510 if (!cret) return -1;
2511 if (!ipc->uploading) return -1;
2513 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2514 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2521 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2522 void (*progress_gauge_callback)
2523 (CtdlIPC*, unsigned long, unsigned long),
2531 FILE *fd = uploadFP;
2534 if (!cret) return -1;
2536 fseek(fd, 0L, SEEK_END);
2540 if (progress_gauge_callback)
2541 progress_gauge_callback(ipc, 0, bytes);
2543 while (offset < bytes) {
2546 /* Read some data in */
2547 to_write = fread(buf, 1, 4096, fd);
2549 if (feof(fd) || ferror(fd)) break;
2551 sprintf(aaa, "WRIT %d", (int)to_write);
2552 CtdlIPC_putline(ipc, aaa);
2553 CtdlIPC_getline(ipc, aaa);
2554 strcpy(cret, &aaa[4]);
2556 if (aaa[0] == '7') {
2557 to_write = extract_long(&aaa[4], 0);
2559 serv_write(ipc, buf, to_write);
2561 if (progress_gauge_callback)
2562 progress_gauge_callback(ipc, offset, bytes);
2563 /* Detect short reads and back up if needed */
2564 /* offset will never be negative anyway */
2565 fseek(fd, (signed)offset, SEEK_SET);
2570 if (progress_gauge_callback)
2571 progress_gauge_callback(ipc, 1, 1);
2574 return (!ferr ? ret : -2);
2579 * Generic command method. This method should handle any server command
2580 * except for CHAT. It takes the following arguments:
2582 * ipc The server to speak with
2583 * command Preformatted command to send to server
2584 * to_send A text or binary file to send to server
2585 * (only sent if server requests it)
2586 * bytes_to_send The number of bytes in to_send (required if
2587 * sending binary, optional if sending listing)
2588 * to_receive Pointer to a NULL pointer, if the server
2589 * sends text or binary we will allocate memory
2590 * for the file and stuff it here
2591 * bytes_to_receive If a file is received, we will store its
2593 * proto_response The protocol response. Caller must provide
2594 * this buffer and ensure that it is at least
2595 * 128 bytes in length.
2597 * This function returns a number equal to the protocol response number,
2598 * -1 if an internal error occurred, -2 if caller provided bad values,
2599 * or 0 - the protocol response number if bad values were found during
2600 * the protocol exchange.
2601 * It stores the protocol response string (minus the number) in
2602 * protocol_response as described above. Some commands send additional
2603 * data in this string.
2605 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2606 const char *command, const char *to_send,
2607 size_t bytes_to_send, char **to_receive,
2608 size_t *bytes_to_receive, char *proto_response)
2613 if (!command) return -2;
2614 if (!proto_response) return -2;
2617 CtdlIPC_putline(ipc, command);
2619 CtdlIPC_getline(ipc, proto_response);
2620 if (proto_response[3] == '*')
2622 ret = atoi(proto_response);
2623 strcpy(proto_response, &proto_response[4]);
2624 switch (ret / 100) {
2625 default: /* Unknown, punt */
2627 case 3: /* MORE_DATA */
2629 /* Don't need to do anything */
2631 case 1: /* LISTING_FOLLOWS */
2632 if (to_receive && !*to_receive && bytes_to_receive) {
2633 *to_receive = CtdlIPCReadListing(ipc, NULL);
2634 } else { /* Drain */
2635 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2639 case 4: /* SEND_LISTING */
2641 CtdlIPCSendListing(ipc, to_send);
2643 /* No listing given, fake it */
2644 CtdlIPC_putline(ipc, "000");
2648 case 6: /* BINARY_FOLLOWS */
2649 if (to_receive && !*to_receive && bytes_to_receive) {
2651 extract_long(proto_response, 0);
2652 *to_receive = (char *)
2653 malloc((size_t)*bytes_to_receive);
2657 serv_read(ipc, *to_receive,
2664 drain = extract_long(proto_response, 0);
2665 while (drain > SIZ) {
2666 serv_read(ipc, buf, SIZ);
2669 serv_read(ipc, buf, drain);
2673 case 7: /* SEND_BINARY */
2674 if (to_send && bytes_to_send) {
2675 serv_write(ipc, to_send, bytes_to_send);
2676 } else if (bytes_to_send) {
2677 /* Fake it, send nulls */
2680 fake = bytes_to_send;
2681 memset(buf, '\0', SIZ);
2682 while (fake > SIZ) {
2683 serv_write(ipc, buf, SIZ);
2686 serv_write(ipc, buf, fake);
2688 } /* else who knows? DANGER WILL ROBINSON */
2690 case 8: /* START_CHAT_MODE */
2691 if (!strncasecmp(command, "CHAT", 4)) {
2692 /* Don't call chatmode with generic! */
2693 CtdlIPC_putline(ipc, "/quit");
2696 /* In this mode we send then receive listing */
2698 CtdlIPCSendListing(ipc, to_send);
2700 /* No listing given, fake it */
2701 CtdlIPC_putline(ipc, "000");
2704 if (to_receive && !*to_receive
2705 && bytes_to_receive) {
2706 *to_receive = CtdlIPCReadListing(ipc, NULL);
2707 } else { /* Drain */
2708 while (CtdlIPC_getline(ipc, buf),
2709 strcmp(buf, "000")) ;
2714 case 9: /* ASYNC_MSG */
2715 /* CtdlIPCDoAsync(ret, proto_response); */
2716 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2722 CtdlIPC_unlock(ipc);
2728 * Connect to a Citadel on a remote host using a TCP/IP socket
2730 static int tcp_connectsock(char *host, char *service)
2732 struct in6_addr serveraddr;
2733 struct addrinfo hints;
2734 struct addrinfo *res = NULL;
2735 struct addrinfo *ai = NULL;
2739 if ((host == NULL) || IsEmptyStr(host)) {
2740 service = DEFAULT_HOST ;
2742 if ((service == NULL) || IsEmptyStr(service)) {
2743 service = DEFAULT_PORT ;
2746 memset(&hints, 0x00, sizeof(hints));
2747 hints.ai_flags = AI_NUMERICSERV;
2748 hints.ai_family = AF_UNSPEC;
2749 hints.ai_socktype = SOCK_STREAM;
2752 * Handle numeric IPv4 and IPv6 addresses
2754 rc = inet_pton(AF_INET, host, &serveraddr);
2755 if (rc == 1) { /* dotted quad */
2756 hints.ai_family = AF_INET;
2757 hints.ai_flags |= AI_NUMERICHOST;
2760 rc = inet_pton(AF_INET6, host, &serveraddr);
2761 if (rc == 1) { /* IPv6 address */
2762 hints.ai_family = AF_INET6;
2763 hints.ai_flags |= AI_NUMERICHOST;
2767 /* Begin the connection process */
2769 rc = getaddrinfo(host, service, &hints, &res);
2775 * Try all available addresses until we connect to one or until we run out.
2777 for (ai = res; ai != NULL; ai = ai->ai_next) {
2778 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2779 if (sock < 0) return(-1);
2781 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2783 return(sock); /* Connected! */
2786 close(sock); /* Failed. Close the socket to avoid fd leak! */
2798 * Connect to a Citadel on the local host using a unix domain socket
2800 static int uds_connectsock(int *isLocal, char *sockpath)
2802 struct sockaddr_un addr;
2805 memset(&addr, 0, sizeof(addr));
2806 addr.sun_family = AF_UNIX;
2807 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2809 s = socket(AF_UNIX, SOCK_STREAM, 0);
2814 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2825 * input binary data from socket
2827 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2829 unsigned int len, rlen;
2831 #if defined(HAVE_OPENSSL)
2833 serv_read_ssl(ipc, buf, bytes);
2838 while (len < bytes) {
2839 rlen = read(ipc->sock, &buf[len], bytes - len);
2841 connection_died(ipc, 0);
2850 * send binary to server
2852 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2854 unsigned int bytes_written = 0;
2857 #if defined(HAVE_OPENSSL)
2859 serv_write_ssl(ipc, buf, nbytes);
2863 while (bytes_written < nbytes) {
2864 retval = write(ipc->sock, &buf[bytes_written],
2865 nbytes - bytes_written);
2867 connection_died(ipc, 0);
2870 bytes_written += retval;
2877 * input binary data from encrypted connection
2879 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2885 while (len < bytes) {
2886 if (SSL_want_read(ipc->ssl)) {
2887 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2888 error_printf("SSL_write in serv_read:\n");
2889 ERR_print_errors_fp(stderr);
2892 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2896 errval = SSL_get_error(ipc->ssl, rlen);
2897 if (errval == SSL_ERROR_WANT_READ ||
2898 errval == SSL_ERROR_WANT_WRITE) {
2903 Not sure why we'd want to handle these error codes any differently,
2904 but this definitely isn't the way to handle them. Someone must have
2905 naively assumed that we could fall back to unencrypted communications,
2906 but all it does is just recursively blow the stack.
2907 if (errval == SSL_ERROR_ZERO_RETURN ||
2908 errval == SSL_ERROR_SSL) {
2909 serv_read(ipc, &buf[len], bytes - len);
2913 error_printf("SSL_read in serv_read: %s\n",
2914 ERR_reason_error_string(ERR_peek_error()));
2915 connection_died(ipc, 1);
2924 * send binary to server encrypted
2926 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2928 unsigned int bytes_written = 0;
2932 while (bytes_written < nbytes) {
2933 if (SSL_want_write(ipc->ssl)) {
2934 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2935 error_printf("SSL_read in serv_write:\n");
2936 ERR_print_errors_fp(stderr);
2939 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2940 nbytes - bytes_written);
2944 errval = SSL_get_error(ipc->ssl, retval);
2945 if (errval == SSL_ERROR_WANT_READ ||
2946 errval == SSL_ERROR_WANT_WRITE) {
2950 if (errval == SSL_ERROR_ZERO_RETURN ||
2951 errval == SSL_ERROR_SSL) {
2952 serv_write(ipc, &buf[bytes_written],
2953 nbytes - bytes_written);
2956 error_printf("SSL_write in serv_write: %s\n",
2957 ERR_reason_error_string(ERR_peek_error()));
2958 connection_died(ipc, 1);
2961 bytes_written += retval;
2966 #ifdef THREADED_CLIENT
2967 static void ssl_lock(int mode, int n, const char *file, int line)
2969 if (mode & CRYPTO_LOCK)
2970 pthread_mutex_lock(Critters[n]);
2972 pthread_mutex_unlock(Critters[n]);
2974 #endif /* THREADED_CLIENT */
2977 static void CtdlIPC_init_OpenSSL(void)
2980 const SSL_METHOD *ssl_method;
2983 /* already done init */
2992 SSL_load_error_strings();
2993 SSLeay_add_ssl_algorithms();
2995 /* Set up the SSL context in which we will oeprate */
2996 ssl_method = SSLv23_client_method();
2997 ssl_ctx = SSL_CTX_new(ssl_method);
2999 error_printf("SSL_CTX_new failed: %s\n",
3000 ERR_reason_error_string(ERR_get_error()));
3003 /* Any reasonable cipher we can get */
3004 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
3005 error_printf("No ciphers available for encryption\n");
3008 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
3010 /* Load DH parameters into the context */
3013 error_printf("Can't allocate a DH object: %s\n",
3014 ERR_reason_error_string(ERR_get_error()));
3017 if (!(BN_hex2bn(&(dh->p), DH_P))) {
3018 error_printf("Can't assign DH_P: %s\n",
3019 ERR_reason_error_string(ERR_get_error()));
3023 if (!(BN_hex2bn(&(dh->g), DH_G))) {
3024 error_printf("Can't assign DH_G: %s\n",
3025 ERR_reason_error_string(ERR_get_error()));
3030 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
3033 #ifdef THREADED_CLIENT
3034 /* OpenSSL requires callbacks for threaded clients */
3035 CRYPTO_set_locking_callback(ssl_lock);
3036 CRYPTO_set_id_callback(id_callback);
3038 /* OpenSSL requires us to do semaphores for threaded clients */
3039 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
3041 perror("malloc failed");
3044 for (a = 0; a < CRYPTO_num_locks(); a++) {
3045 Critters[a] = malloc(sizeof (pthread_mutex_t));
3047 perror("malloc failed");
3050 pthread_mutex_init(Critters[a], NULL);
3053 #endif /* THREADED_CLIENT */
3058 #ifdef THREADED_CLIENT
3059 static unsigned long id_callback(void) {
3060 return (unsigned long)pthread_self();
3062 #endif /* THREADED_CLIENT */
3063 #endif /* HAVE_OPENSSL */
3067 ReadNetworkChunk(CtdlIPC* ipc)
3084 FD_SET(ipc->sock, &read_fd);
3085 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
3087 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
3091 *(ipc->BufPtr) = '\0';
3092 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3093 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3095 ipc->BufPtr[n]='\0';
3103 if (!(errno == EINTR || errno == EAGAIN))
3104 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
3110 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3112 ipc->BufPtr[n]='\0';
3117 connection_died(ipc, 0);
3125 * input string from socket - implemented in terms of serv_read()
3129 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3132 char *aptr, *bptr, *aeptr, *beptr;
3134 // error_printf("---\n");
3137 #if defined(HAVE_OPENSSL)
3140 /* Read one character at a time. */
3142 serv_read(ipc, &buf[i], 1);
3143 if (buf[i] == '\n' || i == (SIZ-1))
3147 /* If we got a long line, discard characters until the newline. */
3149 while (buf[i] != '\n')
3150 serv_read(ipc, &buf[i], 1);
3152 /* Strip the trailing newline (and carriage return, if present) */
3153 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3154 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3159 if (ipc->Buf == NULL)
3162 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3164 ipc->BufPtr = ipc->Buf;
3168 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3169 if (ipc->BufUsed == 0)
3170 ReadNetworkChunk(ipc);
3172 //// if (ipc->BufUsed != 0) while (1)
3178 aeptr = ipc->Buf + ipc->BufSize;
3179 while ((aptr < aeptr) &&
3183 *(bptr++) = *(aptr++);
3184 if ((*aptr == '\n') && (aptr < aeptr))
3186 /* Terminate it right, remove the line breaks */
3187 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3189 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3192 // 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);
3193 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3196 /* is there more in the buffer we need to read later? */
3197 if (ipc->Buf + ipc->BufUsed > aptr)
3204 ipc->BufPtr = ipc->Buf;
3206 // error_printf("----bla6\n");
3209 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3210 else if ((ipc->BufPtr != ipc->Buf) &&
3211 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3213 size_t NewBufSize = ipc->BufSize * 2;
3214 int delta = (ipc->BufPtr - ipc->Buf);
3217 /* if the line would end after our buffer, we should use a bigger buffer. */
3218 NewBuf = (char *)malloc (NewBufSize + 10);
3219 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3221 ipc->Buf = ipc->BufPtr = NewBuf;
3222 ipc->BufUsed -= delta;
3223 ipc->BufSize = NewBufSize;
3225 if (ReadNetworkChunk(ipc) <0)
3227 // error_printf("----bla\n");
3231 /// error_printf("----bl45761%s\nipc->BufUsed");
3233 // error_printf("----bla1\n");
3236 #else /* CHUNKED_READ */
3238 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3242 /* Read one character at a time. */
3244 serv_read(ipc, &buf[i], 1);
3245 if (buf[i] == '\n' || i == (SIZ-1))
3249 /* If we got a long line, discard characters until the newline. */
3251 while (buf[i] != '\n')
3252 serv_read(ipc, &buf[i], 1);
3254 /* Strip the trailing newline (and carriage return, if present) */
3255 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3256 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3260 #endif /* CHUNKED_READ */
3263 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3265 CtdlIPC_getline(ipc, buf);
3269 * send line to server - implemented in terms of serv_write()
3271 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3277 cmd = malloc(len + 2);
3279 /* This requires no extra memory */
3280 serv_write(ipc, buf, len);
3281 serv_write(ipc, "\n", 1);
3283 /* This is network-optimized */
3284 strncpy(cmd, buf, len);
3285 strcpy(cmd + len, "\n");
3286 serv_write(ipc, cmd, len + 1);
3290 ipc->last_command_sent = time(NULL);
3293 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3295 CtdlIPC_putline(ipc, buf);
3302 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3310 ipc = ialloc(CtdlIPC);
3314 #if defined(HAVE_OPENSSL)
3316 CtdlIPC_init_OpenSSL();
3318 #if defined(HAVE_PTHREAD_H)
3319 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3321 ipc->sock = -1; /* Not connected */
3322 ipc->isLocal = 0; /* Not local, of course! */
3323 ipc->downloading = 0;
3325 ipc->last_command_sent = 0L;
3326 ipc->network_status_cb = NULL;
3331 strcpy(cithost, DEFAULT_HOST); /* default host */
3332 strcpy(citport, DEFAULT_PORT); /* default port */
3334 /* Allow caller to supply our values (Windows) */
3335 if (hostbuf && strlen(hostbuf) > 0)
3336 strcpy(cithost, hostbuf);
3337 if (portbuf && strlen(portbuf) > 0)
3338 strcpy(citport, portbuf);
3340 /* Read host/port from command line if present */
3341 for (a = 0; a < argc; ++a) {
3344 } else if (a == 1) {
3345 strcpy(cithost, argv[a]);
3346 } else if (a == 2) {
3347 strcpy(citport, argv[a]);
3349 error_printf("%s: usage: ",argv[0]);
3350 error_printf("%s [host] [port] ",argv[0]);
3357 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3361 /* If we're using a unix domain socket we can do a bunch of stuff */
3362 if (!strcmp(cithost, UDS)) {
3363 if (!strcasecmp(citport, DEFAULT_PORT)) {
3364 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3367 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3369 printf("[%s]\n", sockpath);
3370 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3371 if (ipc->sock == -1) {
3375 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3376 if (portbuf != NULL) strcpy(portbuf, sockpath);
3377 strcpy(ipc->ip_hostname, "");
3378 strcpy(ipc->ip_address, "");
3382 printf("[%s:%s]\n", cithost, citport);
3383 ipc->sock = tcp_connectsock(cithost, citport);
3384 if (ipc->sock == -1) {
3390 /* Learn the actual network identity of the host to which we are connected */
3392 struct sockaddr_in6 clientaddr;
3393 unsigned int addrlen = sizeof(clientaddr);
3395 ipc->ip_hostname[0] = 0;
3396 ipc->ip_address[0] = 0;
3398 getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3399 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3400 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3402 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3403 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3406 /* stuff other things elsewhere */
3408 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3409 if (portbuf != NULL) strcpy(portbuf, citport);
3415 * Disconnect and delete the IPC class (destructor)
3417 void CtdlIPC_delete(CtdlIPC* ipc)
3421 SSL_shutdown(ipc->ssl);
3426 if (ipc->sock > -1) {
3427 shutdown(ipc->sock, 2); /* Close it up */
3430 if (ipc->Buf != NULL)
3439 * Disconnect and delete the IPC class (destructor)
3440 * Also NULLs out the pointer
3442 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3444 CtdlIPC_delete(*pipc);
3450 * return the file descriptor of the server socket so we can select() on it.
3452 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3455 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3462 * return one character
3464 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3467 char CtdlIPC_get(CtdlIPC* ipc)
3472 serv_read(ipc, buf, 1);