1 // Copyright (c) 1987-2020 by the citadel.org team
3 // This program is open source software. Use, duplication, and/or
4 // disclosure are subject to the GNU General Purpose License version 3.
6 // This program is distributed in the hope that it will be useful,
7 // but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // GNU General Public License for more details.
11 #include "textclient.h"
14 static SSL_CTX *ssl_ctx;
18 #endif /* HAVE_OPENSSL */
21 #define INADDR_NONE 0xffffffff
24 static void (*status_hook) (char *s) = NULL;
25 char ctdl_autoetc_dir[PATH_MAX] = "";
26 char file_citadel_rc[PATH_MAX] = "";
27 char ctdl_run_dir[PATH_MAX] = "";
28 char ctdl_etc_dir[PATH_MAX] = "";
29 char ctdl_home_directory[PATH_MAX] = "";
30 char file_citadel_socket[PATH_MAX] = "";
52 void CtdlIPC_lock(CtdlIPC * ipc)
54 if (ipc->network_status_cb)
55 ipc->network_status_cb(1);
59 void CtdlIPC_unlock(CtdlIPC * ipc)
61 if (ipc->network_status_cb)
62 ipc->network_status_cb(0);
66 char *libcitadelclient_version_string(void)
68 return "libcitadelclient(unnumbered)";
74 #define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
75 snprintf(SUBDIR,sizeof SUBDIR, "%s%s%s%s%s%s%s", \
76 (home&!relh)?ctdl_home_directory:basedir, \
77 ((basedir!=ctdldir)&(home&!relh))?basedir:"/", \
78 ((basedir!=ctdldir)&(home&!relh))?"/":"", \
80 (relhome[0]!='\0')?"/":"",\
82 (dirbuffer[0]!='\0')?"/":"");
84 #define DBG_PRINT(A) if (dbg==1) fprintf (stderr,"%s : %s \n", #A, A)
87 void calc_dirs_n_files(int relh, int home, const char *relhome, char *ctdldir, int dbg)
89 const char *basedir = "";
90 char dirbuffer[PATH_MAX] = "";
92 StripSlashes(ctdldir, 1);
99 COMPUTE_DIRECTORY(ctdl_run_dir);
100 StripSlashes(ctdl_run_dir, 1);
103 #ifndef HAVE_AUTO_ETC_DIR
106 basedir = AUTO_ETC_DIR;
108 COMPUTE_DIRECTORY(ctdl_autoetc_dir);
109 StripSlashes(ctdl_autoetc_dir, 1);
117 COMPUTE_DIRECTORY(ctdl_etc_dir);
118 StripSlashes(ctdl_etc_dir, 1);
122 snprintf(file_citadel_rc, sizeof file_citadel_rc, "%scitadel.rc", ctdl_etc_dir);
123 StripSlashes(file_citadel_rc, 0);
125 snprintf(file_citadel_socket, sizeof file_citadel_socket, "%scitadel.socket", ctdl_run_dir);
126 StripSlashes(file_citadel_socket, 0);
128 DBG_PRINT(ctdl_run_dir);
129 DBG_PRINT(file_citadel_socket);
130 DBG_PRINT(ctdl_etc_dir);
131 DBG_PRINT(file_citadel_rc);
134 void setCryptoStatusHook(void (*hook) (char *s))
139 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC * ipc, void (*hook) (int state))
141 ipc->network_status_cb = hook;
145 char instant_msgs = 0;
148 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes);
149 static void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes);
151 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes);
152 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes);
153 static void endtls(SSL * ssl);
154 #endif /* HAVE_OPENSSL */
155 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf);
156 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf);
160 const char *svn_revision(void);
163 * Does nothing. The server should always return 200.
165 int CtdlIPCNoop(CtdlIPC * ipc)
169 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
174 * Does nothing interesting. The server should always return 200
175 * along with your string.
177 int CtdlIPCEcho(CtdlIPC * ipc, const char *arg, char *cret)
187 aaa = (char *) malloc((size_t) (strlen(arg) + 6));
191 sprintf(aaa, "ECHO %s", arg);
192 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
199 * Asks the server to close the connecction.
200 * Should always return 200.
202 int CtdlIPCQuit(CtdlIPC * ipc)
204 int ret = 221; /* Default to successful quit */
208 if (ipc->sock > -1) {
209 CtdlIPC_putline(ipc, "QUIT");
210 CtdlIPC_getline(ipc, aaa);
215 SSL_shutdown(ipc->ssl);
219 shutdown(ipc->sock, 2); /* Close connection; we're dead */
227 * Asks the server to log out. Should always return 200, even if no user
228 * was logged in. The user will not be logged in after this!
230 int CtdlIPCLogout(CtdlIPC * ipc)
236 CtdlIPC_putline(ipc, "LOUT");
237 CtdlIPC_getline(ipc, aaa);
245 * First stage of authentication - pass the username. Returns 300 if the
246 * username is able to log in, with the username correctly spelled in cret.
247 * Returns various 500 error codes if the user doesn't exist, etc.
249 int CtdlIPCTryLogin(CtdlIPC * ipc, const char *username, char *cret)
259 aaa = (char *) malloc((size_t) (strlen(username) + 6));
263 sprintf(aaa, "USER %s", username);
264 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
271 * Second stage of authentication - provide password. The server returns
272 * 200 and several arguments in cret relating to the user's account.
274 int CtdlIPCTryPassword(CtdlIPC * ipc, const char *passwd, char *cret)
284 aaa = (char *) malloc((size_t) (strlen(passwd) + 6));
288 sprintf(aaa, "PASS %s", passwd);
289 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
296 * Second stage of authentication - provide password. The server returns
297 * 200 and several arguments in cret relating to the user's account.
299 int CtdlIPCTryApopPassword(CtdlIPC * ipc, const char *response, char *cret)
309 aaa = (char *) malloc((size_t) (strlen(response) + 6));
313 sprintf(aaa, "PAS2 %s", response);
314 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
321 * Create a new user. This returns 200 plus the same arguments as TryPassword
322 * if selfservice is nonzero, unless there was a problem creating the account.
323 * If selfservice is zero, creates a new user but does not log out the existing
324 * user - intended for use by system administrators to create accounts on
325 * behalf of other users.
327 int CtdlIPCCreateUser(CtdlIPC * ipc, const char *username, int selfservice, char *cret)
337 aaa = (char *) malloc((size_t) (strlen(username) + 6));
341 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
342 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
349 * Changes the user's password. Returns 200 if changed, errors otherwise.
351 int CtdlIPCChangePassword(CtdlIPC * ipc, const char *passwd, char *cret)
361 aaa = (char *) malloc((size_t) (strlen(passwd) + 6));
365 sprintf(aaa, "SETP %s", passwd);
366 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
373 /* Caller must free the march list */
374 /* Room types are defined in enum RoomList; keep these in sync! */
375 /* floor is -1 for all, or floornum */
376 int CtdlIPCKnownRooms(CtdlIPC * ipc, enum RoomList which, int floor, struct march **listing, char *cret)
379 struct march *march = NULL;
380 static char *proto[] = { "LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
388 return -2; /* Free the listing first */
391 /* if (which < 0 || which > 4) return -2; */
393 return -2; /* Can't validate upper bound, sorry */
395 sprintf(aaa, "%s %d", proto[which], floor);
396 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
397 if (ret / 100 == 1) {
400 while (bbb && strlen(bbb)) {
403 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
405 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
406 mptr = (struct march *) malloc(sizeof(struct march));
409 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
410 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
411 mptr->march_floor = (char) extract_int(aaa, 2);
412 mptr->march_order = (char) extract_int(aaa, 3);
413 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
414 mptr->march_access = (char) extract_int(aaa, 5);
421 while (mptr2->next != NULL)
436 /* Caller must free the struct ctdluser; caller may pass an existing one */
437 int CtdlIPCGetConfig(CtdlIPC * ipc, struct ctdluser **uret, char *cret)
446 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
450 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
451 if (ret / 100 == 2) {
452 uret[0]->flags = extract_int(cret, 2);
459 int CtdlIPCSetConfig(CtdlIPC * ipc, struct ctdluser *uret, char *cret)
468 sprintf(aaa, "SETU 80|24|%d", uret->flags);
469 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
474 int CtdlIPCRenameUser(CtdlIPC * ipc, char *oldname, char *newname, char *cret)
486 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
487 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
493 int CtdlIPCGotoRoom(CtdlIPC * ipc, const char *room, const char *passwd, struct ctdlipcroom **rret, char *cret)
503 *rret = (struct ctdlipcroom *) calloc(1, sizeof(struct ctdlipcroom));
508 aaa = (char *) malloc(strlen(room) + strlen(passwd) + 7);
513 sprintf(aaa, "GOTO %s|%s", room, passwd);
515 aaa = (char *) malloc(strlen(room) + 6);
520 sprintf(aaa, "GOTO %s", room);
522 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
523 if (ret / 100 == 2) {
524 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
525 rret[0]->RRunread = extract_long(cret, 1);
526 rret[0]->RRtotal = extract_long(cret, 2);
527 rret[0]->RRinfoupdated = extract_int(cret, 3);
528 rret[0]->RRflags = extract_int(cret, 4);
529 rret[0]->RRhighest = extract_long(cret, 5);
530 rret[0]->RRlastread = extract_long(cret, 6);
531 rret[0]->RRismailbox = extract_int(cret, 7);
532 rret[0]->RRaide = extract_int(cret, 8);
533 rret[0]->RRnewmail = extract_long(cret, 9);
534 rret[0]->RRfloor = extract_int(cret, 10);
535 rret[0]->RRcurrentview = extract_int(cret, 11);
536 rret[0]->RRdefaultview = extract_int(cret, 12);
537 /* position 13 is a trash folder flag ... irrelevant in this client */
538 rret[0]->RRflags2 = extract_int(cret, 14);
549 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
550 /* whicharg is number of messages, applies to last, first, gt, lt */
551 int CtdlIPCGetMessages(CtdlIPC * ipc, enum MessageList which, int whicharg, const char *mtemplate, unsigned long **mret, char *cret)
554 unsigned long count = 0;
555 static char *proto[] = { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
566 if (which < 0 || which > 6)
570 sprintf(aaa, "MSGS %s||%d", proto[which], (mtemplate) ? 1 : 0);
572 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg, (mtemplate) ? 1 : 0);
574 count = strlen(mtemplate);
575 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
579 *mret = (unsigned long *) calloc(1, sizeof(unsigned long));
582 while (bbb && strlen(bbb)) {
583 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
584 remove_token(bbb, 0, '\n');
585 *mret = (unsigned long *) realloc(*mret, (size_t) ((count + 2) * sizeof(unsigned long)));
587 (*mret)[count++] = atol(aaa);
600 int CtdlIPCGetSingleMessage(CtdlIPC * ipc, long msgnum, int headers, int as_mime, struct ctdlipcmessage **mret, char *cret)
606 int multipart_hunting = 0;
607 char multipart_prefix[128];
615 *mret = (struct ctdlipcmessage *) calloc(1, sizeof(struct ctdlipcmessage));
621 strcpy(encoding, "");
622 strcpy(mret[0]->content_type, "");
623 mret[0]->is_local = 0;
624 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
625 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
626 if (ret / 100 == 1) {
628 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
629 while (strlen(bbb) > 4 && bbb[4] == '=') {
630 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
631 remove_token(bbb, 0, '\n');
633 if (!strncasecmp(aaa, "nhdr=yes", 8))
635 else if (!strncasecmp(aaa, "from=", 5))
636 safestrncpy(mret[0]->author, &aaa[5], SIZ);
637 else if (!strncasecmp(aaa, "type=", 5))
638 mret[0]->type = atoi(&aaa[5]);
639 else if (!strncasecmp(aaa, "msgn=", 5))
640 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
641 else if (!strncasecmp(aaa, "subj=", 5))
642 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
643 else if (!strncasecmp(aaa, "rfca=", 5))
644 safestrncpy(mret[0]->email, &aaa[5], SIZ);
645 else if (!strncasecmp(aaa, "room=", 5))
646 safestrncpy(mret[0]->room, &aaa[5], SIZ);
647 else if (!strncasecmp(aaa, "rcpt=", 5))
648 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
649 else if (!strncasecmp(aaa, "wefw=", 5))
650 safestrncpy(mret[0]->references, &aaa[5], SIZ);
651 else if (!strncasecmp(aaa, "time=", 5))
652 mret[0]->time = atol(&aaa[5]);
653 else if (!strncasecmp(aaa, "locl", 4))
654 mret[0]->is_local = 1;
656 /* Multipart/alternative prefix & suffix strings help
657 * us to determine which part we want to download.
659 else if (!strncasecmp(aaa, "pref=", 5)) {
660 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
661 if (!strcasecmp(multipart_prefix, "multipart/alternative")) {
664 } else if (!strncasecmp(aaa, "suff=", 5)) {
665 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
666 if (!strcasecmp(multipart_prefix, "multipart/alternative")) {
671 else if (!strncasecmp(aaa, "part=", 5)) {
672 struct parts *ptr, *chain;
674 ptr = (struct parts *) calloc(1, sizeof(struct parts));
677 /* Fill the buffers for the caller */
678 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
679 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
680 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
681 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
682 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
683 ptr->length = extract_long(&aaa[5], 5);
684 if (!mret[0]->attachments)
685 mret[0]->attachments = ptr;
687 chain = mret[0]->attachments;
693 /* Now handle multipart/alternative */
694 if (multipart_hunting > 0) {
695 if ((!strcasecmp(ptr->mimetype, "text/plain"))
696 || (!strcasecmp(ptr->mimetype, "text/html"))) {
697 strcpy(mret[0]->mime_chosen, ptr->number);
704 /* Eliminate "text\n" */
705 remove_token(bbb, 0, '\n');
707 /* If doing a MIME thing, pull out the extra headers */
710 if (!strncasecmp(bbb, "Content-type:", 13)) {
711 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
712 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
713 striplt(mret[0]->content_type);
715 /* strip out ";charset=" portion. FIXME do something with
716 * the charset (like... convert it) instead of just throwing
719 if (strstr(mret[0]->content_type, ";") != NULL) {
720 strcpy(strstr(mret[0]->content_type, ";"), "");
724 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
725 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
726 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
727 striplt(mret[0]->mime_chosen);
729 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
730 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
731 strcpy(encoding, &encoding[26]);
734 remove_token(bbb, 0, '\n');
735 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
736 remove_token(bbb, 0, '\n');
743 if ((!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable"))) {
745 int bytes_decoded = 0;
746 ccc = malloc(strlen(bbb) + 32768);
747 if (!strcasecmp(encoding, "base64")) {
748 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
749 } else if (!strcasecmp(encoding, "quoted-printable")) {
750 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
752 ccc[bytes_decoded] = 0;
757 /* FIXME: Strip trailing whitespace */
758 bbb = (char *) realloc(bbb, (size_t) (strlen(bbb) + 1));
761 bbb = (char *) realloc(bbb, 1);
771 int CtdlIPCWhoKnowsRoom(CtdlIPC * ipc, char **listing, char *cret)
783 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
789 int CtdlIPCServerInfo(CtdlIPC * ipc, char *cret)
793 char *listing = NULL;
799 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
800 if (ret / 100 == 1) {
803 while (*listing && strlen(listing)) {
804 extract_token(buf, listing, 0, '\n', sizeof buf);
805 remove_token(listing, 0, '\n');
808 ipc->ServInfo.pid = atoi(buf);
811 strcpy(ipc->ServInfo.nodename, buf);
814 strcpy(ipc->ServInfo.humannode, buf);
817 strcpy(ipc->ServInfo.fqdn, buf);
820 strcpy(ipc->ServInfo.software, buf);
823 ipc->ServInfo.rev_level = atoi(buf);
826 strcpy(ipc->ServInfo.site_location, buf);
829 strcpy(ipc->ServInfo.sysadm, buf);
832 strcpy(ipc->ServInfo.moreprompt, buf);
835 ipc->ServInfo.ok_floors = atoi(buf);
838 ipc->ServInfo.paging_level = atoi(buf);
841 ipc->ServInfo.supports_qnop = atoi(buf);
844 ipc->ServInfo.supports_ldap = atoi(buf);
847 ipc->ServInfo.newuser_disabled = atoi(buf);
850 strcpy(ipc->ServInfo.default_cal_zone, buf);
853 ipc->ServInfo.load_avg = atof(buf);
856 ipc->ServInfo.worker_avg = atof(buf);
859 ipc->ServInfo.thread_count = atoi(buf);
862 ipc->ServInfo.has_sieve = atoi(buf);
865 ipc->ServInfo.fulltext_enabled = atoi(buf);
868 strcpy(ipc->ServInfo.svn_revision, buf);
871 ipc->ServInfo.guest_logins = atoi(buf);
884 int CtdlIPCReadDirectory(CtdlIPC * ipc, char **listing, char *cret)
896 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
902 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
904 int CtdlIPCSetLastRead(CtdlIPC * ipc, long msgnum, char *cret)
913 sprintf(aaa, "SLRP %ld", msgnum);
915 sprintf(aaa, "SLRP HIGHEST");
917 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
923 int CtdlIPCInviteUserToRoom(CtdlIPC * ipc, const char *username, char *cret)
933 aaa = (char *) malloc(strlen(username) + 6);
937 sprintf(aaa, "INVT %s", username);
938 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
945 int CtdlIPCKickoutUserFromRoom(CtdlIPC * ipc, const char *username, char *cret)
955 aaa = (char *) malloc(strlen(username) + 6);
957 sprintf(aaa, "KICK %s", username);
958 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
965 int CtdlIPCGetRoomAttributes(CtdlIPC * ipc, struct ctdlroom **qret, char *cret)
974 *qret = (struct ctdlroom *) calloc(1, sizeof(struct ctdlroom));
978 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
979 if (ret / 100 == 2) {
980 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
981 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
982 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
983 qret[0]->QRflags = extract_int(cret, 3);
984 qret[0]->QRfloor = extract_int(cret, 4);
985 qret[0]->QRorder = extract_int(cret, 5);
986 qret[0]->QRdefaultview = extract_int(cret, 6);
987 qret[0]->QRflags2 = extract_int(cret, 7);
994 /* set forget to kick all users out of room */
995 int CtdlIPCSetRoomAttributes(CtdlIPC * ipc, int forget, struct ctdlroom *qret, char *cret)
1005 aaa = (char *) malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) + strlen(qret->QRdirname) + 64);
1009 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
1010 qret->QRname, qret->QRpasswd, qret->QRdirname,
1011 qret->QRflags, forget, qret->QRfloor, qret->QRorder, qret->QRdefaultview, qret->QRflags2);
1012 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1019 int CtdlIPCGetRoomAide(CtdlIPC * ipc, char *cret)
1024 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
1029 int CtdlIPCSetRoomAide(CtdlIPC * ipc, const char *username, char *cret)
1039 aaa = (char *) malloc(strlen(username) + 6);
1043 sprintf(aaa, "SETA %s", username);
1044 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1051 int CtdlIPCPostMessage(CtdlIPC * ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
1062 if (mr->references) {
1063 for (ptr = mr->references; *ptr != 0; ++ptr) {
1069 snprintf(cmd, sizeof cmd,
1070 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
1071 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
1072 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL, NULL, cret);
1073 if ((flag == 0) && (subject_required != NULL)) {
1074 /* Is the server strongly recommending that the user enter a message subject? */
1075 if ((cret[3] != '\0') && (cret[4] != '\0')) {
1076 *subject_required = extract_int(&cret[4], 1);
1086 int CtdlIPCRoomInfo(CtdlIPC * ipc, char **iret, char *cret)
1097 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
1102 int CtdlIPCDeleteMessage(CtdlIPC * ipc, long msgnum, char *cret)
1111 sprintf(aaa, "DELE %ld", msgnum);
1112 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1117 int CtdlIPCMoveMessage(CtdlIPC * ipc, int copy, long msgnum, const char *destroom, char *cret)
1129 aaa = (char *) malloc(strlen(destroom) + 28);
1133 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
1134 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1141 int CtdlIPCDeleteRoom(CtdlIPC * ipc, int for_real, char *cret)
1148 sprintf(aaa, "KILL %d", for_real);
1149 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1154 int CtdlIPCCreateRoom(CtdlIPC * ipc, int for_real, const char *roomname, int type, const char *password, int floor, char *cret)
1165 aaa = (char *) malloc(strlen(roomname) + strlen(password) + 40);
1168 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type, password, floor);
1170 aaa = (char *) malloc(strlen(roomname) + 40);
1173 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type, floor);
1175 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1182 int CtdlIPCForgetRoom(CtdlIPC * ipc, char *cret)
1187 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1192 int CtdlIPCSystemMessage(CtdlIPC * ipc, const char *message, char **mret, char *cret)
1207 aaa = (char *) malloc(strlen(message) + 6);
1211 sprintf(aaa, "MESG %s", message);
1212 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1219 int CtdlIPCNextUnvalidatedUser(CtdlIPC * ipc, char *cret)
1224 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1229 int CtdlIPCGetUserRegistration(CtdlIPC * ipc, const char *username, char **rret, char *cret)
1243 aaa = (char *) malloc(strlen(username) + 6);
1245 aaa = (char *) malloc(12);
1250 sprintf(aaa, "GREG %s", username);
1252 sprintf(aaa, "GREG _SELF_");
1253 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1260 int CtdlIPCValidateUser(CtdlIPC * ipc, const char *username, int axlevel, char *cret)
1269 if (axlevel < AxDeleted || axlevel > AxAideU)
1272 aaa = (char *) malloc(strlen(username) + 17);
1276 sprintf(aaa, "VALI %s|%d", username, axlevel);
1277 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1284 int CtdlIPCSetRoomInfo(CtdlIPC * ipc, int for_real, const char *info, char *cret)
1293 sprintf(aaa, "EINF %d", for_real);
1294 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1299 int CtdlIPCUserListing(CtdlIPC * ipc, char *searchstring, char **listing, char *cret)
1314 cmd = malloc(strlen(searchstring) + 10);
1315 sprintf(cmd, "LIST %s", searchstring);
1317 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1324 int CtdlIPCSetRegistration(CtdlIPC * ipc, const char *info, char *cret)
1331 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info), NULL, NULL, cret);
1336 int CtdlIPCMiscCheck(CtdlIPC * ipc, struct ctdlipcmisc *chek, char *cret)
1345 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1346 if (ret / 100 == 2) {
1347 chek->newmail = extract_long(cret, 0);
1348 chek->needregis = extract_int(cret, 1);
1349 chek->needvalid = extract_int(cret, 2);
1356 int CtdlIPCDeleteFile(CtdlIPC * ipc, const char *filename, char *cret)
1366 aaa = (char *) malloc(strlen(filename) + 6);
1370 sprintf(aaa, "DELF %s", filename);
1371 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1378 int CtdlIPCMoveFile(CtdlIPC * ipc, const char *filename, const char *destroom, char *cret)
1390 aaa = (char *) malloc(strlen(filename) + strlen(destroom) + 7);
1394 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1395 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1402 int CtdlIPCOnlineUsers(CtdlIPC * ipc, char **listing, time_t * stamp, char *cret)
1414 *stamp = CtdlIPCServerTime(ipc, cret);
1416 *stamp = time(NULL);
1417 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1423 int CtdlIPCFileDownload(CtdlIPC * ipc, const char *filename, void **buf, size_t resume, void (*progress_gauge_callback)
1424 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1440 if (ipc->downloading)
1443 aaa = (char *) malloc(strlen(filename) + 6);
1447 sprintf(aaa, "OPEN %s", filename);
1448 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1450 if (ret / 100 == 2) {
1451 ipc->downloading = 1;
1452 bytes = extract_long(cret, 0);
1453 last_mod = extract_int(cret, 1);
1454 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1456 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret);
1458 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1459 progress_gauge_callback, cret);
1462 ret = CtdlIPCEndDownload(ipc, cret);
1464 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1471 int CtdlIPCAttachmentDownload(CtdlIPC * ipc, long msgnum, const char *part, void **buf, void (*progress_gauge_callback)
1472 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1491 if (ipc->downloading)
1494 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1495 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1496 if (ret / 100 == 2) {
1497 ipc->downloading = 1;
1498 bytes = extract_long(cret, 0);
1499 last_mod = extract_int(cret, 1);
1500 extract_token(filename, cret, 2, '|', sizeof filename);
1501 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1502 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1503 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1504 ret = CtdlIPCEndDownload(ipc, cret);
1506 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1513 int CtdlIPCImageDownload(CtdlIPC * ipc, const char *filename, void **buf, void (*progress_gauge_callback)
1514 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1530 if (ipc->downloading)
1533 aaa = (char *) malloc(strlen(filename) + 6);
1537 sprintf(aaa, "OIMG %s", filename);
1538 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1540 if (ret / 100 == 2) {
1541 ipc->downloading = 1;
1542 bytes = extract_long(cret, 0);
1543 last_mod = extract_int(cret, 1);
1544 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1545 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1546 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1547 ret = CtdlIPCEndDownload(ipc, cret);
1549 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1556 int CtdlIPCFileUpload(CtdlIPC * ipc, const char *save_as, const char *comment, const char *path, void (*progress_gauge_callback)
1557 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1562 char MimeTestBuf[64];
1563 const char *MimeType;
1579 uploadFP = fopen(path, "r");
1583 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1588 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1589 aaa = (char *) malloc(strlen(save_as) + strlen(MimeType) + strlen(comment) + 7);
1593 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1594 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1596 if (ret / 100 == 2) {
1598 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1599 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1607 int CtdlIPCImageUpload(CtdlIPC * ipc, int for_real, const char *path, const char *save_as, void (*progress_gauge_callback)
1608 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1613 char MimeTestBuf[64];
1614 const char *MimeType;
1621 if (!path && for_real)
1623 if (!*path && for_real)
1628 aaa = (char *) malloc(strlen(save_as) + 17);
1632 uploadFP = fopen(path, "r");
1636 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1640 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1642 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1643 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1645 if (ret / 100 == 2 && for_real) {
1647 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1648 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1656 int CtdlIPCQueryUsername(CtdlIPC * ipc, const char *username, char *cret)
1666 aaa = (char *) malloc(strlen(username) + 6);
1670 sprintf(aaa, "QUSR %s", username);
1671 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1678 int CtdlIPCFloorListing(CtdlIPC * ipc, char **listing, char *cret)
1689 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1694 int CtdlIPCCreateFloor(CtdlIPC * ipc, int for_real, const char *name, char *cret)
1704 sprintf(aaa, "CFLR %s|%d", name, for_real);
1705 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1711 int CtdlIPCDeleteFloor(CtdlIPC * ipc, int for_real, int floornum, char *cret)
1720 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1721 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1726 int CtdlIPCEditFloor(CtdlIPC * ipc, int floornum, const char *floorname, char *cret)
1738 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1739 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1747 * You only need to fill out hostname, the defaults will be used if any of the
1748 * other fields are not set properly.
1750 int CtdlIPCIdentifySoftware(CtdlIPC * ipc, int developerid, int clientid,
1751 int revision, const char *software_name, const char *hostname, char *cret)
1756 if (developerid < 0 || clientid < 0 || revision < 0 || !software_name) {
1759 revision = CLIENT_VERSION - 600;
1760 software_name = "Citadel (libcitadel)";
1765 aaa = (char *) malloc(strlen(software_name) + strlen(hostname) + 29);
1769 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid, revision, software_name, hostname);
1770 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1777 int CtdlIPCSendInstantMessage(CtdlIPC * ipc, const char *username, const char *text, char *cret)
1787 aaa = (char *) malloc(strlen(username) + 8);
1792 sprintf(aaa, "SEXP %s|-", username);
1793 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1795 sprintf(aaa, "SEXP %s||", username);
1796 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1804 int CtdlIPCGetInstantMessage(CtdlIPC * ipc, char **listing, char *cret)
1815 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1820 /* mode is 0 = enable, 1 = disable, 2 = status */
1821 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC * ipc, int mode, char *cret)
1828 sprintf(aaa, "DEXP %d", mode);
1829 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1834 int CtdlIPCSetBio(CtdlIPC * ipc, char *bio, char *cret)
1841 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio), NULL, NULL, cret);
1846 int CtdlIPCGetBio(CtdlIPC * ipc, const char *username, char **listing, char *cret)
1861 aaa = (char *) malloc(strlen(username) + 6);
1865 sprintf(aaa, "RBIO %s", username);
1866 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1873 int CtdlIPCListUsersWithBios(CtdlIPC * ipc, char **listing, char *cret)
1884 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1889 int CtdlIPCStealthMode(CtdlIPC * ipc, int mode, char *cret)
1896 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1897 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1902 int CtdlIPCTerminateSession(CtdlIPC * ipc, int sid, char *cret)
1909 sprintf(aaa, "TERM %d", sid);
1910 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1915 int CtdlIPCTerminateServerNow(CtdlIPC * ipc, char *cret)
1920 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1925 int CtdlIPCTerminateServerScheduled(CtdlIPC * ipc, int mode, char *cret)
1932 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1933 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1938 int CtdlIPCEnterSystemMessage(CtdlIPC * ipc, const char *filename, const char *text, char *cret)
1950 aaa = (char *) malloc(strlen(filename) + 6);
1954 sprintf(aaa, "EMSG %s", filename);
1955 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1962 /* This function returns the actual server time reported, or 0 if error */
1963 time_t CtdlIPCServerTime(CtdlIPC * ipc, char *cret)
1968 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1969 if (ret / 100 == 2) {
1970 tret = extract_long(cret, 0);
1979 int CtdlIPCAideGetUserParameters(CtdlIPC * ipc, const char *who, struct ctdluser **uret, char *cret)
1989 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
1993 sprintf(aaa, "AGUP %s", who);
1994 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1996 if (ret / 100 == 2) {
1997 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1998 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1999 uret[0]->flags = extract_int(cret, 2);
2000 uret[0]->timescalled = extract_long(cret, 3);
2001 uret[0]->posted = extract_long(cret, 4);
2002 uret[0]->axlevel = extract_int(cret, 5);
2003 uret[0]->usernum = extract_long(cret, 6);
2004 uret[0]->lastcall = extract_long(cret, 7);
2005 uret[0]->USuserpurge = extract_int(cret, 8);
2012 int CtdlIPCAideSetUserParameters(CtdlIPC * ipc, const struct ctdluser *uret, char *cret)
2022 aaa = (char *) malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
2026 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
2027 uret->fullname, uret->password, uret->flags, uret->timescalled,
2028 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge);
2029 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2036 int CtdlIPCAideGetEmailAddresses(CtdlIPC * ipc, const char *who, char *target_buf, char *cret)
2040 char *emailaddrs = NULL;
2041 size_t emailaddrs_len = 0;
2043 sprintf(aaa, "AGEA %s", who);
2044 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
2046 if (ret / 100 == 1) {
2047 strcpy(target_buf, emailaddrs);
2050 if (emailaddrs != NULL) {
2059 int CtdlIPCAideSetEmailAddresses(CtdlIPC * ipc, const char *who, char *emailaddrs, char *cret)
2071 sprintf(aaa, "ASEA %s", who);
2072 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2078 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2079 /* caller must free the struct ExpirePolicy */
2080 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC * ipc, GPEXWhichPolicy which, struct ExpirePolicy **policy, char *cret)
2082 static char *proto[] = {
2086 strof(mailboxespolicy)
2096 *policy = (struct ExpirePolicy *) calloc(1, sizeof(struct ExpirePolicy));
2099 if (which < 0 || which > 3)
2102 sprintf(cmd, "GPEX %s", proto[which]);
2103 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2104 if (ret / 100 == 2) {
2105 policy[0]->expire_mode = extract_int(cret, 0);
2106 policy[0]->expire_value = extract_int(cret, 1);
2113 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2114 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2115 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC * ipc, int which, struct ExpirePolicy *policy, char *cret)
2118 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2122 if (which < 0 || which > 3)
2126 if (policy->expire_mode < 0 || policy->expire_mode > 3)
2128 if (policy->expire_mode >= 2 && policy->expire_value < 1)
2131 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which], policy->expire_mode, policy->expire_value);
2132 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2137 int CtdlIPCGetSystemConfig(CtdlIPC * ipc, char **listing, char *cret)
2148 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0, listing, &bytes, cret);
2153 int CtdlIPCSetSystemConfig(CtdlIPC * ipc, const char *listing, char *cret)
2160 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing), NULL, NULL, cret);
2165 int CtdlIPCGetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, char **listing, char *cret)
2180 aaa = malloc(strlen(mimetype) + 13);
2183 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2184 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2191 int CtdlIPCSetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, const char *listing, char *cret)
2203 aaa = malloc(strlen(mimetype) + 13);
2206 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2207 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2214 int CtdlIPCGetRoomNetworkConfig(CtdlIPC * ipc, char **listing, char *cret)
2225 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2230 int CtdlIPCSetRoomNetworkConfig(CtdlIPC * ipc, const char *listing, char *cret)
2237 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2242 int CtdlIPCRequestClientLogout(CtdlIPC * ipc, int session, char *cret)
2251 sprintf(aaa, "REQT %d", session);
2252 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2257 int CtdlIPCSetMessageSeen(CtdlIPC * ipc, long msgnum, int seen, char *cret)
2266 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2267 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2272 int CtdlIPCStartEncryption(CtdlIPC * ipc, char *cret)
2281 /* New SSL object */
2282 temp_ssl = SSL_new(ssl_ctx);
2284 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2287 /* Pointless flag waving */
2288 #if SSLEAY_VERSION_NUMBER >= 0x0922
2289 SSL_set_session_id_context(temp_ssl, (const unsigned char *) "Citadel SID", 14);
2292 /* Associate network connection with SSL object */
2293 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2294 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2298 if (status_hook != NULL) {
2299 status_hook("Requesting encryption...\r");
2302 /* Ready to start SSL/TLS */
2303 r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2305 error_printf("Server can't start TLS: %s\n", buf);
2310 /* Do SSL/TLS handshake */
2311 if ((a = SSL_connect(temp_ssl)) < 1) {
2312 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2316 ipc->ssl = temp_ssl;
2318 error_printf("Encrypting with %s cipher %s\n",
2319 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)), SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2324 #endif /* HAVE_OPENSSL */
2329 static void endtls(SSL * ssl)
2340 int CtdlIPCDirectoryLookup(CtdlIPC * ipc, const char *address, char *cret)
2350 aaa = (char *) malloc(strlen(address) + 6);
2354 sprintf(aaa, "QDIR %s", address);
2355 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2362 int CtdlIPCInternalProgram(CtdlIPC * ipc, int secret, char *cret)
2368 sprintf(aaa, "IPGM %d", secret);
2369 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2375 /* ************************************************************************** */
2376 /* Stuff below this line is not for public consumption */
2377 /* ************************************************************************** */
2380 /* Read a listing from the server up to 000. Append to dest if it exists */
2381 char *CtdlIPCReadListing(CtdlIPC * ipc, char *dest)
2390 length = strlen(ret);
2395 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2396 linelength = strlen(aaa);
2397 ret = (char *) realloc(ret, (size_t) (length + linelength + 2));
2399 strcpy(&ret[length], aaa);
2400 length += linelength;
2401 strcpy(&ret[length++], "\n");
2409 /* Send a listing to the server; generate the ending 000. */
2410 int CtdlIPCSendListing(CtdlIPC * ipc, const char *listing)
2414 text = (char *) malloc(strlen(listing) + 6);
2416 strcpy(text, listing);
2417 while (text[strlen(text) - 1] == '\n')
2418 text[strlen(text) - 1] = '\0';
2419 strcat(text, "\n000");
2420 CtdlIPC_putline(ipc, text);
2424 /* Malloc failed but we are committed to send */
2425 /* This may result in extra blanks at the bottom */
2426 CtdlIPC_putline(ipc, text);
2427 CtdlIPC_putline(ipc, "000");
2433 /* Partial read of file from server */
2434 size_t CtdlIPCPartialRead(CtdlIPC * ipc, void **buf, size_t offset, size_t bytes, char *cret)
2447 sprintf(aaa, "READ %d|%d", (int) offset, (int) bytes);
2448 CtdlIPC_putline(ipc, aaa);
2449 CtdlIPC_getline(ipc, aaa);
2451 strcpy(cret, &aaa[4]);
2453 len = extract_long(&aaa[4], 0);
2454 *buf = (void *) realloc(*buf, (size_t) (offset + len));
2456 /* I know what I'm doing */
2457 serv_read(ipc, ((char *) (*buf) + offset), len);
2459 /* We have to read regardless */
2460 serv_read(ipc, aaa, len);
2464 CtdlIPC_unlock(ipc);
2470 int CtdlIPCEndDownload(CtdlIPC * ipc, char *cret)
2476 if (!ipc->downloading)
2479 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2481 ipc->downloading = 0;
2487 int CtdlIPCSpecifyPreferredFormats(CtdlIPC * ipc, char *cret, char *formats)
2492 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2493 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2500 int CtdlIPCReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2501 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2511 if (!ipc->downloading)
2515 if (progress_gauge_callback)
2516 progress_gauge_callback(ipc, len, bytes);
2517 while (len < bytes) {
2520 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2526 if (progress_gauge_callback)
2527 progress_gauge_callback(ipc, len, bytes);
2532 /* READ - pipelined */
2533 int CtdlIPCHighSpeedReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2534 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2537 int calls; /* How many calls in the pipeline */
2538 int i; /* iterator */
2547 if (!ipc->downloading)
2550 *buf = (void *) realloc(*buf, bytes - resume);
2556 if (progress_gauge_callback)
2557 progress_gauge_callback(ipc, len, bytes);
2559 /* How many calls will be in the pipeline? */
2560 calls = (bytes - resume) / 4096;
2561 if ((bytes - resume) % 4096)
2564 /* Send all requests at once */
2565 for (i = 0; i < calls; i++) {
2566 sprintf(aaa, "READ %d|4096", (int) (i * 4096 + resume));
2567 CtdlIPC_putline(ipc, aaa);
2570 /* Receive all responses at once */
2571 for (i = 0; i < calls; i++) {
2572 CtdlIPC_getline(ipc, aaa);
2574 strcpy(cret, &aaa[4]);
2576 len = extract_long(&aaa[4], 0);
2577 /* I know what I'm doing */
2578 serv_read(ipc, ((char *) (*buf) + (i * 4096)), len);
2580 if (progress_gauge_callback)
2581 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2583 CtdlIPC_unlock(ipc);
2589 int CtdlIPCEndUpload(CtdlIPC * ipc, int discard, char *cret)
2596 if (!ipc->uploading)
2599 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2600 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2607 int CtdlIPCWriteUpload(CtdlIPC * ipc, FILE * uploadFP, void (*progress_gauge_callback)
2608 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2615 FILE *fd = uploadFP;
2621 fseek(fd, 0L, SEEK_END);
2625 if (progress_gauge_callback)
2626 progress_gauge_callback(ipc, 0, bytes);
2628 while (offset < bytes) {
2631 /* Read some data in */
2632 to_write = fread(buf, 1, 4096, fd);
2634 if (feof(fd) || ferror(fd))
2637 sprintf(aaa, "WRIT %d", (int) to_write);
2638 CtdlIPC_putline(ipc, aaa);
2639 CtdlIPC_getline(ipc, aaa);
2640 strcpy(cret, &aaa[4]);
2642 if (aaa[0] == '7') {
2643 to_write = extract_long(&aaa[4], 0);
2645 serv_write(ipc, buf, to_write);
2647 if (progress_gauge_callback)
2648 progress_gauge_callback(ipc, offset, bytes);
2649 /* Detect short reads and back up if needed */
2650 /* offset will never be negative anyway */
2651 fseek(fd, (signed) offset, SEEK_SET);
2656 if (progress_gauge_callback)
2657 progress_gauge_callback(ipc, 1, 1);
2660 return (!ferr ? ret : -2);
2665 * Generic command method. This method should handle any server command
2666 * except for CHAT. It takes the following arguments:
2668 * ipc The server to speak with
2669 * command Preformatted command to send to server
2670 * to_send A text or binary file to send to server
2671 * (only sent if server requests it)
2672 * bytes_to_send The number of bytes in to_send (required if
2673 * sending binary, optional if sending listing)
2674 * to_receive Pointer to a NULL pointer, if the server
2675 * sends text or binary we will allocate memory
2676 * for the file and stuff it here
2677 * bytes_to_receive If a file is received, we will store its
2679 * proto_response The protocol response. Caller must provide
2680 * this buffer and ensure that it is at least
2681 * 128 bytes in length.
2683 * This function returns a number equal to the protocol response number,
2684 * -1 if an internal error occurred, -2 if caller provided bad values,
2685 * or 0 - the protocol response number if bad values were found during
2686 * the protocol exchange.
2687 * It stores the protocol response string (minus the number) in
2688 * protocol_response as described above. Some commands send additional
2689 * data in this string.
2691 int CtdlIPCGenericCommand(CtdlIPC * ipc,
2692 const char *command, const char *to_send,
2693 size_t bytes_to_send, char **to_receive, size_t * bytes_to_receive, char *proto_response)
2700 if (!proto_response)
2704 CtdlIPC_putline(ipc, command);
2706 CtdlIPC_getline(ipc, proto_response);
2707 if (proto_response[3] == '*')
2709 ret = atoi(proto_response);
2710 strcpy(proto_response, &proto_response[4]);
2711 switch (ret / 100) {
2712 default: /* Unknown, punt */
2714 case 3: /* MORE_DATA */
2716 /* Don't need to do anything */
2718 case 1: /* LISTING_FOLLOWS */
2719 if (to_receive && !*to_receive && bytes_to_receive) {
2720 *to_receive = CtdlIPCReadListing(ipc, NULL);
2721 } else { /* Drain */
2722 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2726 case 4: /* SEND_LISTING */
2728 CtdlIPCSendListing(ipc, to_send);
2730 /* No listing given, fake it */
2731 CtdlIPC_putline(ipc, "000");
2735 case 6: /* BINARY_FOLLOWS */
2736 if (to_receive && !*to_receive && bytes_to_receive) {
2737 *bytes_to_receive = extract_long(proto_response, 0);
2738 *to_receive = (char *)
2739 malloc((size_t) * bytes_to_receive);
2743 serv_read(ipc, *to_receive, *bytes_to_receive);
2749 drain = extract_long(proto_response, 0);
2750 while (drain > SIZ) {
2751 serv_read(ipc, buf, SIZ);
2754 serv_read(ipc, buf, drain);
2758 case 7: /* SEND_BINARY */
2759 if (to_send && bytes_to_send) {
2760 serv_write(ipc, to_send, bytes_to_send);
2761 } else if (bytes_to_send) {
2762 /* Fake it, send nulls */
2765 fake = bytes_to_send;
2766 memset(buf, '\0', SIZ);
2767 while (fake > SIZ) {
2768 serv_write(ipc, buf, SIZ);
2771 serv_write(ipc, buf, fake);
2773 } /* else who knows? DANGER WILL ROBINSON */
2775 case 8: /* START_CHAT_MODE */
2776 if (!strncasecmp(command, "CHAT", 4)) {
2777 /* Don't call chatmode with generic! */
2778 CtdlIPC_putline(ipc, "/quit");
2781 /* In this mode we send then receive listing */
2783 CtdlIPCSendListing(ipc, to_send);
2785 /* No listing given, fake it */
2786 CtdlIPC_putline(ipc, "000");
2789 if (to_receive && !*to_receive && bytes_to_receive) {
2790 *to_receive = CtdlIPCReadListing(ipc, NULL);
2791 } else { /* Drain */
2792 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2797 case 9: /* ASYNC_MSG */
2798 /* CtdlIPCDoAsync(ret, proto_response); */
2799 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2805 CtdlIPC_unlock(ipc);
2811 * Connect to a Citadel on a remote host using a TCP/IP socket
2813 static int tcp_connectsock(char *host, char *service)
2815 struct in6_addr serveraddr;
2816 struct addrinfo hints;
2817 struct addrinfo *res = NULL;
2818 struct addrinfo *ai = NULL;
2822 if ((host == NULL) || IsEmptyStr(host)) {
2823 service = DEFAULT_HOST;
2825 if ((service == NULL) || IsEmptyStr(service)) {
2826 service = DEFAULT_PORT;
2829 memset(&hints, 0x00, sizeof(hints));
2830 hints.ai_flags = AI_NUMERICSERV;
2831 hints.ai_family = AF_UNSPEC;
2832 hints.ai_socktype = SOCK_STREAM;
2835 * Handle numeric IPv4 and IPv6 addresses
2837 rc = inet_pton(AF_INET, host, &serveraddr);
2838 if (rc == 1) { /* dotted quad */
2839 hints.ai_family = AF_INET;
2840 hints.ai_flags |= AI_NUMERICHOST;
2842 rc = inet_pton(AF_INET6, host, &serveraddr);
2843 if (rc == 1) { /* IPv6 address */
2844 hints.ai_family = AF_INET6;
2845 hints.ai_flags |= AI_NUMERICHOST;
2849 /* Begin the connection process */
2851 rc = getaddrinfo(host, service, &hints, &res);
2857 * Try all available addresses until we connect to one or until we run out.
2859 for (ai = res; ai != NULL; ai = ai->ai_next) {
2860 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2864 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2866 return (sock); /* Connected! */
2868 close(sock); /* Failed. Close the socket to avoid fd leak! */
2880 * Connect to a Citadel on the local host using a unix domain socket
2882 static int uds_connectsock(int *isLocal, char *sockpath)
2884 struct sockaddr_un addr;
2887 memset(&addr, 0, sizeof(addr));
2888 addr.sun_family = AF_UNIX;
2889 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2891 s = socket(AF_UNIX, SOCK_STREAM, 0);
2896 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2907 * input binary data from socket
2909 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes)
2911 unsigned int len, rlen;
2913 #if defined(HAVE_OPENSSL)
2915 serv_read_ssl(ipc, buf, bytes);
2920 while (len < bytes) {
2921 rlen = read(ipc->sock, &buf[len], bytes - len);
2923 connection_died(ipc, 0);
2932 * send binary to server
2934 void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
2936 unsigned int bytes_written = 0;
2939 #if defined(HAVE_OPENSSL)
2941 serv_write_ssl(ipc, buf, nbytes);
2945 while (bytes_written < nbytes) {
2946 retval = write(ipc->sock, &buf[bytes_written], nbytes - bytes_written);
2948 connection_died(ipc, 0);
2951 bytes_written += retval;
2958 * input binary data from encrypted connection
2960 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes)
2966 while (len < bytes) {
2967 if (SSL_want_read(ipc->ssl)) {
2968 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2969 error_printf("SSL_write in serv_read:\n");
2970 ERR_print_errors_fp(stderr);
2973 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2977 errval = SSL_get_error(ipc->ssl, rlen);
2978 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
2983 Not sure why we'd want to handle these error codes any differently,
2984 but this definitely isn't the way to handle them. Someone must have
2985 naively assumed that we could fall back to unencrypted communications,
2986 but all it does is just recursively blow the stack.
2987 if (errval == SSL_ERROR_ZERO_RETURN ||
2988 errval == SSL_ERROR_SSL) {
2989 serv_read(ipc, &buf[len], bytes - len);
2993 error_printf("SSL_read in serv_read: %s\n", ERR_reason_error_string(ERR_peek_error()));
2994 connection_died(ipc, 1);
3003 * send binary to server encrypted
3005 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
3007 unsigned int bytes_written = 0;
3011 while (bytes_written < nbytes) {
3012 if (SSL_want_write(ipc->ssl)) {
3013 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
3014 error_printf("SSL_read in serv_write:\n");
3015 ERR_print_errors_fp(stderr);
3018 retval = SSL_write(ipc->ssl, &buf[bytes_written], nbytes - bytes_written);
3022 errval = SSL_get_error(ipc->ssl, retval);
3023 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
3027 if (errval == SSL_ERROR_ZERO_RETURN || errval == SSL_ERROR_SSL) {
3028 serv_write(ipc, &buf[bytes_written], nbytes - bytes_written);
3031 error_printf("SSL_write in serv_write: %s\n", ERR_reason_error_string(ERR_peek_error()));
3032 connection_died(ipc, 1);
3035 bytes_written += retval;
3042 static void CtdlIPC_init_OpenSSL(void)
3045 const SSL_METHOD *ssl_method;
3048 /* already done init */
3057 SSL_load_error_strings();
3058 SSLeay_add_ssl_algorithms();
3060 /* Set up the SSL context in which we will oeprate */
3061 ssl_method = SSLv23_client_method();
3062 ssl_ctx = SSL_CTX_new(ssl_method);
3064 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
3067 /* Any reasonable cipher we can get */
3068 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
3069 error_printf("No ciphers available for encryption\n");
3072 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
3074 /* Load DH parameters into the context */
3077 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
3081 if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
3082 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
3087 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
3091 #endif /* HAVE_OPENSSL */
3094 int ReadNetworkChunk(CtdlIPC * ipc)
3109 FD_SET(ipc->sock, &read_fd);
3110 ret = select(ipc->sock + 1, &read_fd, NULL, NULL, &tv);
3114 *(ipc->BufPtr) = '\0';
3115 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3117 ipc->BufPtr[n] = '\0';
3122 } else if (ret < 0) {
3123 if (!(errno == EINTR || errno == EAGAIN))
3124 error_printf("\nselect failed: %d %s\n", err, strerror(err));
3130 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3132 ipc->BufPtr[n]='\0';
3137 connection_died(ipc, 0);
3145 * input string from socket - implemented in terms of serv_read()
3149 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3152 char *aptr, *bptr, *aeptr, *beptr;
3154 // error_printf("---\n");
3157 #if defined(HAVE_OPENSSL)
3160 /* Read one character at a time. */
3162 serv_read(ipc, &buf[i], 1);
3163 if (buf[i] == '\n' || i == (SIZ - 1))
3167 /* If we got a long line, discard characters until the newline. */
3169 while (buf[i] != '\n')
3170 serv_read(ipc, &buf[i], 1);
3172 /* Strip the trailing newline (and carriage return, if present) */
3173 if (i >= 0 && buf[i] == 10)
3175 if (i >= 0 && buf[i] == 13)
3180 if (ipc->Buf == NULL) {
3182 ipc->Buf = (char *) malloc(ipc->BufSize + 10);
3184 ipc->BufPtr = ipc->Buf;
3188 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3189 if (ipc->BufUsed == 0)
3190 ReadNetworkChunk(ipc);
3192 //// if (ipc->BufUsed != 0) while (1)
3197 aeptr = ipc->Buf + ipc->BufSize;
3198 while ((aptr < aeptr) && (bptr < beptr) && (*aptr != '\0') && (*aptr != '\n'))
3199 *(bptr++) = *(aptr++);
3200 if ((*aptr == '\n') && (aptr < aeptr)) {
3201 /* Terminate it right, remove the line breaks */
3202 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3204 while ((aptr < aeptr) && (*(aptr + 1) == '\0'))
3207 // 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);
3208 if ((bptr > buf + 1) && (*(bptr - 1) == '\r'))
3211 /* is there more in the buffer we need to read later? */
3212 if (ipc->Buf + ipc->BufUsed > aptr) {
3216 ipc->BufPtr = ipc->Buf;
3218 // error_printf("----bla6\n");
3221 } /* should we move our read stuf to the bufferstart so we have more space at the end? */
3222 else if ((ipc->BufPtr != ipc->Buf) && (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4)))) {
3223 size_t NewBufSize = ipc->BufSize * 2;
3224 int delta = (ipc->BufPtr - ipc->Buf);
3227 /* if the line would end after our buffer, we should use a bigger buffer. */
3228 NewBuf = (char *) malloc(NewBufSize + 10);
3229 memcpy(NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3231 ipc->Buf = ipc->BufPtr = NewBuf;
3232 ipc->BufUsed -= delta;
3233 ipc->BufSize = NewBufSize;
3235 if (ReadNetworkChunk(ipc) < 0) {
3236 // error_printf("----bla\n");
3240 /// error_printf("----bl45761%s\nipc->BufUsed");
3242 // error_printf("----bla1\n");
3245 #else /* CHUNKED_READ */
3247 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3251 /* Read one character at a time. */
3253 serv_read(ipc, &buf[i], 1);
3254 if (buf[i] == '\n' || i == (SIZ - 1))
3258 /* If we got a long line, discard characters until the newline. */
3260 while (buf[i] != '\n')
3261 serv_read(ipc, &buf[i], 1);
3263 /* Strip the trailing newline (and carriage return, if present) */
3264 if (i >= 0 && buf[i] == 10)
3266 if (i >= 0 && buf[i] == 13)
3271 #endif /* CHUNKED_READ */
3274 void CtdlIPC_chat_recv(CtdlIPC * ipc, char *buf)
3276 CtdlIPC_getline(ipc, buf);
3280 * send line to server - implemented in terms of serv_write()
3282 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf)
3288 cmd = malloc(len + 2);
3290 /* This requires no extra memory */
3291 serv_write(ipc, buf, len);
3292 serv_write(ipc, "\n", 1);
3294 /* This is network-optimized */
3295 strncpy(cmd, buf, len);
3296 strcpy(cmd + len, "\n");
3297 serv_write(ipc, cmd, len + 1);
3301 ipc->last_command_sent = time(NULL);
3304 void CtdlIPC_chat_send(CtdlIPC * ipc, const char *buf)
3306 CtdlIPC_putline(ipc, buf);
3313 CtdlIPC *CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3321 ipc = malloc(sizeof(struct _CtdlIPC));
3325 #if defined(HAVE_OPENSSL)
3327 CtdlIPC_init_OpenSSL();
3329 ipc->sock = -1; /* Not connected */
3330 ipc->isLocal = 0; /* Not local, of course! */
3331 ipc->downloading = 0;
3333 ipc->last_command_sent = 0L;
3334 ipc->network_status_cb = NULL;
3339 strcpy(cithost, DEFAULT_HOST); /* default host */
3340 strcpy(citport, DEFAULT_PORT); /* default port */
3342 /* Allow caller to supply our values */
3343 if (hostbuf && strlen(hostbuf) > 0) {
3344 strcpy(cithost, hostbuf);
3346 if (portbuf && strlen(portbuf) > 0) {
3347 strcpy(citport, portbuf);
3350 /* Read host/port from command line if present */
3351 for (a = 0; a < argc; ++a) {
3354 } else if (a == 1) {
3355 strcpy(cithost, argv[a]);
3356 } else if (a == 2) {
3357 strcpy(citport, argv[a]);
3359 error_printf("%s: usage: ", argv[0]);
3360 error_printf("%s [host] [port] ", argv[0]);
3367 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3371 /* If we're using a unix domain socket we can do a bunch of stuff */
3372 if (!strcmp(cithost, UDS)) {
3373 if (!strcasecmp(citport, DEFAULT_PORT)) {
3374 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3376 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3378 printf("[%s]\n", sockpath);
3379 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3380 if (ipc->sock == -1) {
3384 if (hostbuf != NULL)
3385 strcpy(hostbuf, cithost);
3386 if (portbuf != NULL)
3387 strcpy(portbuf, sockpath);
3388 strcpy(ipc->ip_hostname, "");
3389 strcpy(ipc->ip_address, "");
3393 printf("[%s:%s]\n", cithost, citport);
3394 ipc->sock = tcp_connectsock(cithost, citport);
3395 if (ipc->sock == -1) {
3401 /* Learn the actual network identity of the host to which we are connected */
3403 struct sockaddr_in6 clientaddr;
3404 unsigned int addrlen = sizeof(clientaddr);
3406 ipc->ip_hostname[0] = 0;
3407 ipc->ip_address[0] = 0;
3409 getpeername(ipc->sock, (struct sockaddr *) &clientaddr, &addrlen);
3410 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0);
3411 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST);
3413 /* stuff other things elsewhere */
3415 if (hostbuf != NULL)
3416 strcpy(hostbuf, cithost);
3417 if (portbuf != NULL)
3418 strcpy(portbuf, citport);
3424 * Disconnect and delete the IPC class (destructor)
3426 void CtdlIPC_delete(CtdlIPC * ipc)
3430 SSL_shutdown(ipc->ssl);
3435 if (ipc->sock > -1) {
3436 shutdown(ipc->sock, 2); /* Close it up */
3439 if (ipc->Buf != NULL)
3448 * Disconnect and delete the IPC class (destructor)
3449 * Also NULLs out the pointer
3451 void CtdlIPC_delete_ptr(CtdlIPC ** pipc)
3453 CtdlIPC_delete(*pipc);
3459 * return the file descriptor of the server socket so we can select() on it.
3461 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3464 int CtdlIPC_getsockfd(CtdlIPC * ipc)
3471 * return one character
3473 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3476 char CtdlIPC_get(CtdlIPC * ipc)
3481 serv_read(ipc, buf, 1);