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.supports_qnop = atoi(buf);
841 ipc->ServInfo.supports_ldap = atoi(buf);
844 ipc->ServInfo.newuser_disabled = atoi(buf);
847 strcpy(ipc->ServInfo.default_cal_zone, buf);
850 ipc->ServInfo.load_avg = atof(buf);
853 ipc->ServInfo.worker_avg = atof(buf);
856 ipc->ServInfo.thread_count = atoi(buf);
859 ipc->ServInfo.has_sieve = atoi(buf);
862 ipc->ServInfo.fulltext_enabled = atoi(buf);
865 strcpy(ipc->ServInfo.svn_revision, buf);
868 ipc->ServInfo.guest_logins = atoi(buf);
881 int CtdlIPCReadDirectory(CtdlIPC * ipc, char **listing, char *cret)
893 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
899 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
901 int CtdlIPCSetLastRead(CtdlIPC * ipc, long msgnum, char *cret)
910 sprintf(aaa, "SLRP %ld", msgnum);
912 sprintf(aaa, "SLRP HIGHEST");
914 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
920 int CtdlIPCInviteUserToRoom(CtdlIPC * ipc, const char *username, char *cret)
930 aaa = (char *) malloc(strlen(username) + 6);
934 sprintf(aaa, "INVT %s", username);
935 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
942 int CtdlIPCKickoutUserFromRoom(CtdlIPC * ipc, const char *username, char *cret)
952 aaa = (char *) malloc(strlen(username) + 6);
954 sprintf(aaa, "KICK %s", username);
955 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
962 int CtdlIPCGetRoomAttributes(CtdlIPC * ipc, struct ctdlroom **qret, char *cret)
971 *qret = (struct ctdlroom *) calloc(1, sizeof(struct ctdlroom));
975 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
976 if (ret / 100 == 2) {
977 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
978 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
979 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
980 qret[0]->QRflags = extract_int(cret, 3);
981 qret[0]->QRfloor = extract_int(cret, 4);
982 qret[0]->QRorder = extract_int(cret, 5);
983 qret[0]->QRdefaultview = extract_int(cret, 6);
984 qret[0]->QRflags2 = extract_int(cret, 7);
991 /* set forget to kick all users out of room */
992 int CtdlIPCSetRoomAttributes(CtdlIPC * ipc, int forget, struct ctdlroom *qret, char *cret)
1002 aaa = (char *) malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) + strlen(qret->QRdirname) + 64);
1006 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
1007 qret->QRname, qret->QRpasswd, qret->QRdirname,
1008 qret->QRflags, forget, qret->QRfloor, qret->QRorder, qret->QRdefaultview, qret->QRflags2);
1009 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1016 int CtdlIPCGetRoomAide(CtdlIPC * ipc, char *cret)
1021 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
1026 int CtdlIPCSetRoomAide(CtdlIPC * ipc, const char *username, char *cret)
1036 aaa = (char *) malloc(strlen(username) + 6);
1040 sprintf(aaa, "SETA %s", username);
1041 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1048 int CtdlIPCPostMessage(CtdlIPC * ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
1059 if (mr->references) {
1060 for (ptr = mr->references; *ptr != 0; ++ptr) {
1066 snprintf(cmd, sizeof cmd,
1067 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
1068 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
1069 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL, NULL, cret);
1070 if ((flag == 0) && (subject_required != NULL)) {
1071 /* Is the server strongly recommending that the user enter a message subject? */
1072 if ((cret[3] != '\0') && (cret[4] != '\0')) {
1073 *subject_required = extract_int(&cret[4], 1);
1083 int CtdlIPCRoomInfo(CtdlIPC * ipc, char **iret, char *cret)
1094 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
1099 int CtdlIPCDeleteMessage(CtdlIPC * ipc, long msgnum, char *cret)
1108 sprintf(aaa, "DELE %ld", msgnum);
1109 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1114 int CtdlIPCMoveMessage(CtdlIPC * ipc, int copy, long msgnum, const char *destroom, char *cret)
1126 aaa = (char *) malloc(strlen(destroom) + 28);
1130 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
1131 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1138 int CtdlIPCDeleteRoom(CtdlIPC * ipc, int for_real, char *cret)
1145 sprintf(aaa, "KILL %d", for_real);
1146 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1151 int CtdlIPCCreateRoom(CtdlIPC * ipc, int for_real, const char *roomname, int type, const char *password, int floor, char *cret)
1162 aaa = (char *) malloc(strlen(roomname) + strlen(password) + 40);
1165 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type, password, floor);
1167 aaa = (char *) malloc(strlen(roomname) + 40);
1170 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type, floor);
1172 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1179 int CtdlIPCForgetRoom(CtdlIPC * ipc, char *cret)
1184 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1189 int CtdlIPCSystemMessage(CtdlIPC * ipc, const char *message, char **mret, char *cret)
1204 aaa = (char *) malloc(strlen(message) + 6);
1208 sprintf(aaa, "MESG %s", message);
1209 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1216 int CtdlIPCNextUnvalidatedUser(CtdlIPC * ipc, char *cret)
1221 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1226 int CtdlIPCGetUserRegistration(CtdlIPC * ipc, const char *username, char **rret, char *cret)
1240 aaa = (char *) malloc(strlen(username) + 6);
1242 aaa = (char *) malloc(12);
1247 sprintf(aaa, "GREG %s", username);
1249 sprintf(aaa, "GREG _SELF_");
1250 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1257 int CtdlIPCValidateUser(CtdlIPC * ipc, const char *username, int axlevel, char *cret)
1266 if (axlevel < AxDeleted || axlevel > AxAideU)
1269 aaa = (char *) malloc(strlen(username) + 17);
1273 sprintf(aaa, "VALI %s|%d", username, axlevel);
1274 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1281 int CtdlIPCSetRoomInfo(CtdlIPC * ipc, int for_real, const char *info, char *cret)
1290 sprintf(aaa, "EINF %d", for_real);
1291 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1296 int CtdlIPCUserListing(CtdlIPC * ipc, char *searchstring, char **listing, char *cret)
1311 cmd = malloc(strlen(searchstring) + 10);
1312 sprintf(cmd, "LIST %s", searchstring);
1314 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1321 int CtdlIPCSetRegistration(CtdlIPC * ipc, const char *info, char *cret)
1328 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info), NULL, NULL, cret);
1333 int CtdlIPCMiscCheck(CtdlIPC * ipc, struct ctdlipcmisc *chek, char *cret)
1342 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1343 if (ret / 100 == 2) {
1344 chek->newmail = extract_long(cret, 0);
1345 chek->needregis = extract_int(cret, 1);
1346 chek->needvalid = extract_int(cret, 2);
1353 int CtdlIPCDeleteFile(CtdlIPC * ipc, const char *filename, char *cret)
1363 aaa = (char *) malloc(strlen(filename) + 6);
1367 sprintf(aaa, "DELF %s", filename);
1368 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1375 int CtdlIPCMoveFile(CtdlIPC * ipc, const char *filename, const char *destroom, char *cret)
1387 aaa = (char *) malloc(strlen(filename) + strlen(destroom) + 7);
1391 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1392 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1399 int CtdlIPCOnlineUsers(CtdlIPC * ipc, char **listing, time_t * stamp, char *cret)
1411 *stamp = CtdlIPCServerTime(ipc, cret);
1413 *stamp = time(NULL);
1414 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1420 int CtdlIPCFileDownload(CtdlIPC * ipc, const char *filename, void **buf, size_t resume, void (*progress_gauge_callback)
1421 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1437 if (ipc->downloading)
1440 aaa = (char *) malloc(strlen(filename) + 6);
1444 sprintf(aaa, "OPEN %s", filename);
1445 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1447 if (ret / 100 == 2) {
1448 ipc->downloading = 1;
1449 bytes = extract_long(cret, 0);
1450 last_mod = extract_int(cret, 1);
1451 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1453 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret);
1455 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1456 progress_gauge_callback, cret);
1459 ret = CtdlIPCEndDownload(ipc, cret);
1461 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1468 int CtdlIPCAttachmentDownload(CtdlIPC * ipc, long msgnum, const char *part, void **buf, void (*progress_gauge_callback)
1469 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1488 if (ipc->downloading)
1491 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1492 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1493 if (ret / 100 == 2) {
1494 ipc->downloading = 1;
1495 bytes = extract_long(cret, 0);
1496 last_mod = extract_int(cret, 1);
1497 extract_token(filename, cret, 2, '|', sizeof filename);
1498 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1499 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1500 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1501 ret = CtdlIPCEndDownload(ipc, cret);
1503 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1510 int CtdlIPCImageDownload(CtdlIPC * ipc, const char *filename, void **buf, void (*progress_gauge_callback)
1511 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1527 if (ipc->downloading)
1530 aaa = (char *) malloc(strlen(filename) + 6);
1534 sprintf(aaa, "OIMG %s", filename);
1535 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1537 if (ret / 100 == 2) {
1538 ipc->downloading = 1;
1539 bytes = extract_long(cret, 0);
1540 last_mod = extract_int(cret, 1);
1541 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1542 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1543 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1544 ret = CtdlIPCEndDownload(ipc, cret);
1546 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1553 int CtdlIPCFileUpload(CtdlIPC * ipc, const char *save_as, const char *comment, const char *path, void (*progress_gauge_callback)
1554 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1559 char MimeTestBuf[64];
1560 const char *MimeType;
1576 uploadFP = fopen(path, "r");
1580 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1585 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1586 aaa = (char *) malloc(strlen(save_as) + strlen(MimeType) + strlen(comment) + 7);
1590 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1591 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1593 if (ret / 100 == 2) {
1595 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1596 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1604 int CtdlIPCImageUpload(CtdlIPC * ipc, int for_real, const char *path, const char *save_as, void (*progress_gauge_callback)
1605 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1610 char MimeTestBuf[64];
1611 const char *MimeType;
1618 if (!path && for_real)
1620 if (!*path && for_real)
1625 aaa = (char *) malloc(strlen(save_as) + 17);
1629 uploadFP = fopen(path, "r");
1633 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1637 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1639 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1640 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1642 if (ret / 100 == 2 && for_real) {
1644 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1645 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1653 int CtdlIPCQueryUsername(CtdlIPC * ipc, const char *username, char *cret)
1663 aaa = (char *) malloc(strlen(username) + 6);
1667 sprintf(aaa, "QUSR %s", username);
1668 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1675 int CtdlIPCFloorListing(CtdlIPC * ipc, char **listing, char *cret)
1686 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1691 int CtdlIPCCreateFloor(CtdlIPC * ipc, int for_real, const char *name, char *cret)
1701 sprintf(aaa, "CFLR %s|%d", name, for_real);
1702 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1708 int CtdlIPCDeleteFloor(CtdlIPC * ipc, int for_real, int floornum, char *cret)
1717 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1718 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1723 int CtdlIPCEditFloor(CtdlIPC * ipc, int floornum, const char *floorname, char *cret)
1735 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1736 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1744 * You only need to fill out hostname, the defaults will be used if any of the
1745 * other fields are not set properly.
1747 int CtdlIPCIdentifySoftware(CtdlIPC * ipc, int developerid, int clientid,
1748 int revision, const char *software_name, const char *hostname, char *cret)
1753 if (developerid < 0 || clientid < 0 || revision < 0 || !software_name) {
1756 revision = CLIENT_VERSION - 600;
1757 software_name = "Citadel (libcitadel)";
1762 aaa = (char *) malloc(strlen(software_name) + strlen(hostname) + 29);
1766 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid, revision, software_name, hostname);
1767 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1774 int CtdlIPCSendInstantMessage(CtdlIPC * ipc, const char *username, const char *text, char *cret)
1784 aaa = (char *) malloc(strlen(username) + 8);
1789 sprintf(aaa, "SEXP %s|-", username);
1790 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1792 sprintf(aaa, "SEXP %s||", username);
1793 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1801 int CtdlIPCGetInstantMessage(CtdlIPC * ipc, char **listing, char *cret)
1812 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1817 /* mode is 0 = enable, 1 = disable, 2 = status */
1818 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC * ipc, int mode, char *cret)
1825 sprintf(aaa, "DEXP %d", mode);
1826 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1831 int CtdlIPCSetBio(CtdlIPC * ipc, char *bio, char *cret)
1838 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio), NULL, NULL, cret);
1843 int CtdlIPCGetBio(CtdlIPC * ipc, const char *username, char **listing, char *cret)
1858 aaa = (char *) malloc(strlen(username) + 6);
1862 sprintf(aaa, "RBIO %s", username);
1863 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1870 int CtdlIPCListUsersWithBios(CtdlIPC * ipc, char **listing, char *cret)
1881 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1886 int CtdlIPCStealthMode(CtdlIPC * ipc, int mode, char *cret)
1893 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1894 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1899 int CtdlIPCTerminateSession(CtdlIPC * ipc, int sid, char *cret)
1906 sprintf(aaa, "TERM %d", sid);
1907 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1912 int CtdlIPCTerminateServerNow(CtdlIPC * ipc, char *cret)
1917 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1922 int CtdlIPCTerminateServerScheduled(CtdlIPC * ipc, int mode, char *cret)
1929 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1930 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1935 int CtdlIPCEnterSystemMessage(CtdlIPC * ipc, const char *filename, const char *text, char *cret)
1947 aaa = (char *) malloc(strlen(filename) + 6);
1951 sprintf(aaa, "EMSG %s", filename);
1952 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1959 /* This function returns the actual server time reported, or 0 if error */
1960 time_t CtdlIPCServerTime(CtdlIPC * ipc, char *cret)
1965 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1966 if (ret / 100 == 2) {
1967 tret = extract_long(cret, 0);
1976 int CtdlIPCAideGetUserParameters(CtdlIPC * ipc, const char *who, struct ctdluser **uret, char *cret)
1986 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
1990 sprintf(aaa, "AGUP %s", who);
1991 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1993 if (ret / 100 == 2) {
1994 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1995 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1996 uret[0]->flags = extract_int(cret, 2);
1997 uret[0]->timescalled = extract_long(cret, 3);
1998 uret[0]->posted = extract_long(cret, 4);
1999 uret[0]->axlevel = extract_int(cret, 5);
2000 uret[0]->usernum = extract_long(cret, 6);
2001 uret[0]->lastcall = extract_long(cret, 7);
2002 uret[0]->USuserpurge = extract_int(cret, 8);
2009 int CtdlIPCAideSetUserParameters(CtdlIPC * ipc, const struct ctdluser *uret, char *cret)
2019 aaa = (char *) malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
2023 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
2024 uret->fullname, uret->password, uret->flags, uret->timescalled,
2025 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge);
2026 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2033 int CtdlIPCAideGetEmailAddresses(CtdlIPC * ipc, const char *who, char *target_buf, char *cret)
2037 char *emailaddrs = NULL;
2038 size_t emailaddrs_len = 0;
2040 sprintf(aaa, "AGEA %s", who);
2041 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
2043 if (ret / 100 == 1) {
2044 strcpy(target_buf, emailaddrs);
2047 if (emailaddrs != NULL) {
2056 int CtdlIPCAideSetEmailAddresses(CtdlIPC * ipc, const char *who, char *emailaddrs, char *cret)
2068 sprintf(aaa, "ASEA %s", who);
2069 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2075 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2076 /* caller must free the struct ExpirePolicy */
2077 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC * ipc, GPEXWhichPolicy which, struct ExpirePolicy **policy, char *cret)
2079 static char *proto[] = {
2083 strof(mailboxespolicy)
2093 *policy = (struct ExpirePolicy *) calloc(1, sizeof(struct ExpirePolicy));
2096 if (which < 0 || which > 3)
2099 sprintf(cmd, "GPEX %s", proto[which]);
2100 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2101 if (ret / 100 == 2) {
2102 policy[0]->expire_mode = extract_int(cret, 0);
2103 policy[0]->expire_value = extract_int(cret, 1);
2110 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2111 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2112 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC * ipc, int which, struct ExpirePolicy *policy, char *cret)
2115 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2119 if (which < 0 || which > 3)
2123 if (policy->expire_mode < 0 || policy->expire_mode > 3)
2125 if (policy->expire_mode >= 2 && policy->expire_value < 1)
2128 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which], policy->expire_mode, policy->expire_value);
2129 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2134 int CtdlIPCGetSystemConfig(CtdlIPC * ipc, char **listing, char *cret)
2145 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0, listing, &bytes, cret);
2150 int CtdlIPCSetSystemConfig(CtdlIPC * ipc, const char *listing, char *cret)
2157 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing), NULL, NULL, cret);
2162 int CtdlIPCGetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, char **listing, char *cret)
2177 aaa = malloc(strlen(mimetype) + 13);
2180 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2181 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2188 int CtdlIPCSetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, const char *listing, char *cret)
2200 aaa = malloc(strlen(mimetype) + 13);
2203 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2204 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2211 int CtdlIPCGetRoomNetworkConfig(CtdlIPC * ipc, char **listing, char *cret)
2222 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2227 int CtdlIPCSetRoomNetworkConfig(CtdlIPC * ipc, const char *listing, char *cret)
2234 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2239 int CtdlIPCRequestClientLogout(CtdlIPC * ipc, int session, char *cret)
2248 sprintf(aaa, "REQT %d", session);
2249 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2254 int CtdlIPCSetMessageSeen(CtdlIPC * ipc, long msgnum, int seen, char *cret)
2263 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2264 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2269 int CtdlIPCStartEncryption(CtdlIPC * ipc, char *cret)
2278 /* New SSL object */
2279 temp_ssl = SSL_new(ssl_ctx);
2281 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2284 /* Pointless flag waving */
2285 #if SSLEAY_VERSION_NUMBER >= 0x0922
2286 SSL_set_session_id_context(temp_ssl, (const unsigned char *) "Citadel SID", 14);
2289 /* Associate network connection with SSL object */
2290 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2291 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2295 if (status_hook != NULL) {
2296 status_hook("Requesting encryption...\r");
2299 /* Ready to start SSL/TLS */
2300 r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2302 error_printf("Server can't start TLS: %s\n", buf);
2307 /* Do SSL/TLS handshake */
2308 if ((a = SSL_connect(temp_ssl)) < 1) {
2309 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2313 ipc->ssl = temp_ssl;
2315 error_printf("Encrypting with %s cipher %s\n",
2316 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)), SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2321 #endif /* HAVE_OPENSSL */
2326 static void endtls(SSL * ssl)
2337 int CtdlIPCDirectoryLookup(CtdlIPC * ipc, const char *address, char *cret)
2347 aaa = (char *) malloc(strlen(address) + 6);
2351 sprintf(aaa, "QDIR %s", address);
2352 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2359 int CtdlIPCInternalProgram(CtdlIPC * ipc, int secret, char *cret)
2365 sprintf(aaa, "IPGM %d", secret);
2366 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2372 /* ************************************************************************** */
2373 /* Stuff below this line is not for public consumption */
2374 /* ************************************************************************** */
2377 /* Read a listing from the server up to 000. Append to dest if it exists */
2378 char *CtdlIPCReadListing(CtdlIPC * ipc, char *dest)
2387 length = strlen(ret);
2392 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2393 linelength = strlen(aaa);
2394 ret = (char *) realloc(ret, (size_t) (length + linelength + 2));
2396 strcpy(&ret[length], aaa);
2397 length += linelength;
2398 strcpy(&ret[length++], "\n");
2406 /* Send a listing to the server; generate the ending 000. */
2407 int CtdlIPCSendListing(CtdlIPC * ipc, const char *listing)
2411 text = (char *) malloc(strlen(listing) + 6);
2413 strcpy(text, listing);
2414 while (text[strlen(text) - 1] == '\n')
2415 text[strlen(text) - 1] = '\0';
2416 strcat(text, "\n000");
2417 CtdlIPC_putline(ipc, text);
2421 /* Malloc failed but we are committed to send */
2422 /* This may result in extra blanks at the bottom */
2423 CtdlIPC_putline(ipc, text);
2424 CtdlIPC_putline(ipc, "000");
2430 /* Partial read of file from server */
2431 size_t CtdlIPCPartialRead(CtdlIPC * ipc, void **buf, size_t offset, size_t bytes, char *cret)
2444 sprintf(aaa, "READ %d|%d", (int) offset, (int) bytes);
2445 CtdlIPC_putline(ipc, aaa);
2446 CtdlIPC_getline(ipc, aaa);
2448 strcpy(cret, &aaa[4]);
2450 len = extract_long(&aaa[4], 0);
2451 *buf = (void *) realloc(*buf, (size_t) (offset + len));
2453 /* I know what I'm doing */
2454 serv_read(ipc, ((char *) (*buf) + offset), len);
2456 /* We have to read regardless */
2457 serv_read(ipc, aaa, len);
2461 CtdlIPC_unlock(ipc);
2467 int CtdlIPCEndDownload(CtdlIPC * ipc, char *cret)
2473 if (!ipc->downloading)
2476 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2478 ipc->downloading = 0;
2484 int CtdlIPCSpecifyPreferredFormats(CtdlIPC * ipc, char *cret, char *formats)
2489 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2490 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2497 int CtdlIPCReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2498 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2508 if (!ipc->downloading)
2512 if (progress_gauge_callback)
2513 progress_gauge_callback(ipc, len, bytes);
2514 while (len < bytes) {
2517 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2523 if (progress_gauge_callback)
2524 progress_gauge_callback(ipc, len, bytes);
2529 /* READ - pipelined */
2530 int CtdlIPCHighSpeedReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2531 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2534 int calls; /* How many calls in the pipeline */
2535 int i; /* iterator */
2544 if (!ipc->downloading)
2547 *buf = (void *) realloc(*buf, bytes - resume);
2553 if (progress_gauge_callback)
2554 progress_gauge_callback(ipc, len, bytes);
2556 /* How many calls will be in the pipeline? */
2557 calls = (bytes - resume) / 4096;
2558 if ((bytes - resume) % 4096)
2561 /* Send all requests at once */
2562 for (i = 0; i < calls; i++) {
2563 sprintf(aaa, "READ %d|4096", (int) (i * 4096 + resume));
2564 CtdlIPC_putline(ipc, aaa);
2567 /* Receive all responses at once */
2568 for (i = 0; i < calls; i++) {
2569 CtdlIPC_getline(ipc, aaa);
2571 strcpy(cret, &aaa[4]);
2573 len = extract_long(&aaa[4], 0);
2574 /* I know what I'm doing */
2575 serv_read(ipc, ((char *) (*buf) + (i * 4096)), len);
2577 if (progress_gauge_callback)
2578 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2580 CtdlIPC_unlock(ipc);
2586 int CtdlIPCEndUpload(CtdlIPC * ipc, int discard, char *cret)
2593 if (!ipc->uploading)
2596 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2597 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2604 int CtdlIPCWriteUpload(CtdlIPC * ipc, FILE * uploadFP, void (*progress_gauge_callback)
2605 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2612 FILE *fd = uploadFP;
2618 fseek(fd, 0L, SEEK_END);
2622 if (progress_gauge_callback)
2623 progress_gauge_callback(ipc, 0, bytes);
2625 while (offset < bytes) {
2628 /* Read some data in */
2629 to_write = fread(buf, 1, 4096, fd);
2631 if (feof(fd) || ferror(fd))
2634 sprintf(aaa, "WRIT %d", (int) to_write);
2635 CtdlIPC_putline(ipc, aaa);
2636 CtdlIPC_getline(ipc, aaa);
2637 strcpy(cret, &aaa[4]);
2639 if (aaa[0] == '7') {
2640 to_write = extract_long(&aaa[4], 0);
2642 serv_write(ipc, buf, to_write);
2644 if (progress_gauge_callback)
2645 progress_gauge_callback(ipc, offset, bytes);
2646 /* Detect short reads and back up if needed */
2647 /* offset will never be negative anyway */
2648 fseek(fd, (signed) offset, SEEK_SET);
2653 if (progress_gauge_callback)
2654 progress_gauge_callback(ipc, 1, 1);
2657 return (!ferr ? ret : -2);
2662 * Generic command method. This method should handle any server command
2663 * except for CHAT. It takes the following arguments:
2665 * ipc The server to speak with
2666 * command Preformatted command to send to server
2667 * to_send A text or binary file to send to server
2668 * (only sent if server requests it)
2669 * bytes_to_send The number of bytes in to_send (required if
2670 * sending binary, optional if sending listing)
2671 * to_receive Pointer to a NULL pointer, if the server
2672 * sends text or binary we will allocate memory
2673 * for the file and stuff it here
2674 * bytes_to_receive If a file is received, we will store its
2676 * proto_response The protocol response. Caller must provide
2677 * this buffer and ensure that it is at least
2678 * 128 bytes in length.
2680 * This function returns a number equal to the protocol response number,
2681 * -1 if an internal error occurred, -2 if caller provided bad values,
2682 * or 0 - the protocol response number if bad values were found during
2683 * the protocol exchange.
2684 * It stores the protocol response string (minus the number) in
2685 * protocol_response as described above. Some commands send additional
2686 * data in this string.
2688 int CtdlIPCGenericCommand(CtdlIPC * ipc,
2689 const char *command, const char *to_send,
2690 size_t bytes_to_send, char **to_receive, size_t * bytes_to_receive, char *proto_response)
2697 if (!proto_response)
2701 CtdlIPC_putline(ipc, command);
2703 CtdlIPC_getline(ipc, proto_response);
2704 if (proto_response[3] == '*')
2706 ret = atoi(proto_response);
2707 strcpy(proto_response, &proto_response[4]);
2708 switch (ret / 100) {
2709 default: /* Unknown, punt */
2711 case 3: /* MORE_DATA */
2713 /* Don't need to do anything */
2715 case 1: /* LISTING_FOLLOWS */
2716 if (to_receive && !*to_receive && bytes_to_receive) {
2717 *to_receive = CtdlIPCReadListing(ipc, NULL);
2718 } else { /* Drain */
2719 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2723 case 4: /* SEND_LISTING */
2725 CtdlIPCSendListing(ipc, to_send);
2727 /* No listing given, fake it */
2728 CtdlIPC_putline(ipc, "000");
2732 case 6: /* BINARY_FOLLOWS */
2733 if (to_receive && !*to_receive && bytes_to_receive) {
2734 *bytes_to_receive = extract_long(proto_response, 0);
2735 *to_receive = (char *)
2736 malloc((size_t) * bytes_to_receive);
2740 serv_read(ipc, *to_receive, *bytes_to_receive);
2746 drain = extract_long(proto_response, 0);
2747 while (drain > SIZ) {
2748 serv_read(ipc, buf, SIZ);
2751 serv_read(ipc, buf, drain);
2755 case 7: /* SEND_BINARY */
2756 if (to_send && bytes_to_send) {
2757 serv_write(ipc, to_send, bytes_to_send);
2758 } else if (bytes_to_send) {
2759 /* Fake it, send nulls */
2762 fake = bytes_to_send;
2763 memset(buf, '\0', SIZ);
2764 while (fake > SIZ) {
2765 serv_write(ipc, buf, SIZ);
2768 serv_write(ipc, buf, fake);
2770 } /* else who knows? DANGER WILL ROBINSON */
2772 case 8: /* START_CHAT_MODE */
2773 if (!strncasecmp(command, "CHAT", 4)) {
2774 /* Don't call chatmode with generic! */
2775 CtdlIPC_putline(ipc, "/quit");
2778 /* In this mode we send then receive listing */
2780 CtdlIPCSendListing(ipc, to_send);
2782 /* No listing given, fake it */
2783 CtdlIPC_putline(ipc, "000");
2786 if (to_receive && !*to_receive && bytes_to_receive) {
2787 *to_receive = CtdlIPCReadListing(ipc, NULL);
2788 } else { /* Drain */
2789 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2794 case 9: /* ASYNC_MSG */
2795 /* CtdlIPCDoAsync(ret, proto_response); */
2796 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2802 CtdlIPC_unlock(ipc);
2808 * Connect to a Citadel on a remote host using a TCP/IP socket
2810 static int tcp_connectsock(char *host, char *service)
2812 struct in6_addr serveraddr;
2813 struct addrinfo hints;
2814 struct addrinfo *res = NULL;
2815 struct addrinfo *ai = NULL;
2819 if ((host == NULL) || IsEmptyStr(host)) {
2820 service = DEFAULT_HOST;
2822 if ((service == NULL) || IsEmptyStr(service)) {
2823 service = DEFAULT_PORT;
2826 memset(&hints, 0x00, sizeof(hints));
2827 hints.ai_flags = AI_NUMERICSERV;
2828 hints.ai_family = AF_UNSPEC;
2829 hints.ai_socktype = SOCK_STREAM;
2832 * Handle numeric IPv4 and IPv6 addresses
2834 rc = inet_pton(AF_INET, host, &serveraddr);
2835 if (rc == 1) { /* dotted quad */
2836 hints.ai_family = AF_INET;
2837 hints.ai_flags |= AI_NUMERICHOST;
2839 rc = inet_pton(AF_INET6, host, &serveraddr);
2840 if (rc == 1) { /* IPv6 address */
2841 hints.ai_family = AF_INET6;
2842 hints.ai_flags |= AI_NUMERICHOST;
2846 /* Begin the connection process */
2848 rc = getaddrinfo(host, service, &hints, &res);
2854 * Try all available addresses until we connect to one or until we run out.
2856 for (ai = res; ai != NULL; ai = ai->ai_next) {
2857 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2861 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2863 return (sock); /* Connected! */
2865 close(sock); /* Failed. Close the socket to avoid fd leak! */
2877 * Connect to a Citadel on the local host using a unix domain socket
2879 static int uds_connectsock(int *isLocal, char *sockpath)
2881 struct sockaddr_un addr;
2884 memset(&addr, 0, sizeof(addr));
2885 addr.sun_family = AF_UNIX;
2886 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2888 s = socket(AF_UNIX, SOCK_STREAM, 0);
2893 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2904 * input binary data from socket
2906 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes)
2908 unsigned int len, rlen;
2910 #if defined(HAVE_OPENSSL)
2912 serv_read_ssl(ipc, buf, bytes);
2917 while (len < bytes) {
2918 rlen = read(ipc->sock, &buf[len], bytes - len);
2920 connection_died(ipc, 0);
2929 * send binary to server
2931 void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
2933 unsigned int bytes_written = 0;
2936 #if defined(HAVE_OPENSSL)
2938 serv_write_ssl(ipc, buf, nbytes);
2942 while (bytes_written < nbytes) {
2943 retval = write(ipc->sock, &buf[bytes_written], nbytes - bytes_written);
2945 connection_died(ipc, 0);
2948 bytes_written += retval;
2955 * input binary data from encrypted connection
2957 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes)
2963 while (len < bytes) {
2964 if (SSL_want_read(ipc->ssl)) {
2965 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2966 error_printf("SSL_write in serv_read:\n");
2967 ERR_print_errors_fp(stderr);
2970 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2974 errval = SSL_get_error(ipc->ssl, rlen);
2975 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
2980 Not sure why we'd want to handle these error codes any differently,
2981 but this definitely isn't the way to handle them. Someone must have
2982 naively assumed that we could fall back to unencrypted communications,
2983 but all it does is just recursively blow the stack.
2984 if (errval == SSL_ERROR_ZERO_RETURN ||
2985 errval == SSL_ERROR_SSL) {
2986 serv_read(ipc, &buf[len], bytes - len);
2990 error_printf("SSL_read in serv_read: %s\n", ERR_reason_error_string(ERR_peek_error()));
2991 connection_died(ipc, 1);
3000 * send binary to server encrypted
3002 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
3004 unsigned int bytes_written = 0;
3008 while (bytes_written < nbytes) {
3009 if (SSL_want_write(ipc->ssl)) {
3010 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
3011 error_printf("SSL_read in serv_write:\n");
3012 ERR_print_errors_fp(stderr);
3015 retval = SSL_write(ipc->ssl, &buf[bytes_written], nbytes - bytes_written);
3019 errval = SSL_get_error(ipc->ssl, retval);
3020 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
3024 if (errval == SSL_ERROR_ZERO_RETURN || errval == SSL_ERROR_SSL) {
3025 serv_write(ipc, &buf[bytes_written], nbytes - bytes_written);
3028 error_printf("SSL_write in serv_write: %s\n", ERR_reason_error_string(ERR_peek_error()));
3029 connection_died(ipc, 1);
3032 bytes_written += retval;
3039 static void CtdlIPC_init_OpenSSL(void)
3042 const SSL_METHOD *ssl_method;
3045 /* already done init */
3054 SSL_load_error_strings();
3055 SSLeay_add_ssl_algorithms();
3057 /* Set up the SSL context in which we will oeprate */
3058 ssl_method = SSLv23_client_method();
3059 ssl_ctx = SSL_CTX_new(ssl_method);
3061 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
3064 /* Any reasonable cipher we can get */
3065 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
3066 error_printf("No ciphers available for encryption\n");
3069 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
3071 /* Load DH parameters into the context */
3074 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
3078 if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
3079 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
3084 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
3088 #endif /* HAVE_OPENSSL */
3091 int ReadNetworkChunk(CtdlIPC * ipc)
3106 FD_SET(ipc->sock, &read_fd);
3107 ret = select(ipc->sock + 1, &read_fd, NULL, NULL, &tv);
3111 *(ipc->BufPtr) = '\0';
3112 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3114 ipc->BufPtr[n] = '\0';
3119 } else if (ret < 0) {
3120 if (!(errno == EINTR || errno == EAGAIN))
3121 error_printf("\nselect failed: %d %s\n", err, strerror(err));
3127 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3129 ipc->BufPtr[n]='\0';
3134 connection_died(ipc, 0);
3142 * input string from socket - implemented in terms of serv_read()
3146 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3149 char *aptr, *bptr, *aeptr, *beptr;
3151 // error_printf("---\n");
3154 #if defined(HAVE_OPENSSL)
3157 /* Read one character at a time. */
3159 serv_read(ipc, &buf[i], 1);
3160 if (buf[i] == '\n' || i == (SIZ - 1))
3164 /* If we got a long line, discard characters until the newline. */
3166 while (buf[i] != '\n')
3167 serv_read(ipc, &buf[i], 1);
3169 /* Strip the trailing newline (and carriage return, if present) */
3170 if (i >= 0 && buf[i] == 10)
3172 if (i >= 0 && buf[i] == 13)
3177 if (ipc->Buf == NULL) {
3179 ipc->Buf = (char *) malloc(ipc->BufSize + 10);
3181 ipc->BufPtr = ipc->Buf;
3185 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3186 if (ipc->BufUsed == 0)
3187 ReadNetworkChunk(ipc);
3189 //// if (ipc->BufUsed != 0) while (1)
3194 aeptr = ipc->Buf + ipc->BufSize;
3195 while ((aptr < aeptr) && (bptr < beptr) && (*aptr != '\0') && (*aptr != '\n'))
3196 *(bptr++) = *(aptr++);
3197 if ((*aptr == '\n') && (aptr < aeptr)) {
3198 /* Terminate it right, remove the line breaks */
3199 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3201 while ((aptr < aeptr) && (*(aptr + 1) == '\0'))
3204 // 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);
3205 if ((bptr > buf + 1) && (*(bptr - 1) == '\r'))
3208 /* is there more in the buffer we need to read later? */
3209 if (ipc->Buf + ipc->BufUsed > aptr) {
3213 ipc->BufPtr = ipc->Buf;
3215 // error_printf("----bla6\n");
3218 } /* should we move our read stuf to the bufferstart so we have more space at the end? */
3219 else if ((ipc->BufPtr != ipc->Buf) && (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4)))) {
3220 size_t NewBufSize = ipc->BufSize * 2;
3221 int delta = (ipc->BufPtr - ipc->Buf);
3224 /* if the line would end after our buffer, we should use a bigger buffer. */
3225 NewBuf = (char *) malloc(NewBufSize + 10);
3226 memcpy(NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3228 ipc->Buf = ipc->BufPtr = NewBuf;
3229 ipc->BufUsed -= delta;
3230 ipc->BufSize = NewBufSize;
3232 if (ReadNetworkChunk(ipc) < 0) {
3233 // error_printf("----bla\n");
3237 /// error_printf("----bl45761%s\nipc->BufUsed");
3239 // error_printf("----bla1\n");
3242 #else /* CHUNKED_READ */
3244 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3248 /* Read one character at a time. */
3250 serv_read(ipc, &buf[i], 1);
3251 if (buf[i] == '\n' || i == (SIZ - 1))
3255 /* If we got a long line, discard characters until the newline. */
3257 while (buf[i] != '\n')
3258 serv_read(ipc, &buf[i], 1);
3260 /* Strip the trailing newline (and carriage return, if present) */
3261 if (i >= 0 && buf[i] == 10)
3263 if (i >= 0 && buf[i] == 13)
3268 #endif /* CHUNKED_READ */
3271 void CtdlIPC_chat_recv(CtdlIPC * ipc, char *buf)
3273 CtdlIPC_getline(ipc, buf);
3277 * send line to server - implemented in terms of serv_write()
3279 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf)
3285 cmd = malloc(len + 2);
3287 /* This requires no extra memory */
3288 serv_write(ipc, buf, len);
3289 serv_write(ipc, "\n", 1);
3291 /* This is network-optimized */
3292 strncpy(cmd, buf, len);
3293 strcpy(cmd + len, "\n");
3294 serv_write(ipc, cmd, len + 1);
3298 ipc->last_command_sent = time(NULL);
3301 void CtdlIPC_chat_send(CtdlIPC * ipc, const char *buf)
3303 CtdlIPC_putline(ipc, buf);
3310 CtdlIPC *CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3318 ipc = malloc(sizeof(struct _CtdlIPC));
3322 #if defined(HAVE_OPENSSL)
3324 CtdlIPC_init_OpenSSL();
3326 ipc->sock = -1; /* Not connected */
3327 ipc->isLocal = 0; /* Not local, of course! */
3328 ipc->downloading = 0;
3330 ipc->last_command_sent = 0L;
3331 ipc->network_status_cb = NULL;
3336 strcpy(cithost, DEFAULT_HOST); /* default host */
3337 strcpy(citport, DEFAULT_PORT); /* default port */
3339 /* Allow caller to supply our values */
3340 if (hostbuf && strlen(hostbuf) > 0) {
3341 strcpy(cithost, hostbuf);
3343 if (portbuf && strlen(portbuf) > 0) {
3344 strcpy(citport, portbuf);
3347 /* Read host/port from command line if present */
3348 for (a = 0; a < argc; ++a) {
3351 } else if (a == 1) {
3352 strcpy(cithost, argv[a]);
3353 } else if (a == 2) {
3354 strcpy(citport, argv[a]);
3356 error_printf("%s: usage: ", argv[0]);
3357 error_printf("%s [host] [port] ", argv[0]);
3364 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3368 /* If we're using a unix domain socket we can do a bunch of stuff */
3369 if (!strcmp(cithost, UDS)) {
3370 if (!strcasecmp(citport, DEFAULT_PORT)) {
3371 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3373 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3375 printf("[%s]\n", sockpath);
3376 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3377 if (ipc->sock == -1) {
3381 if (hostbuf != NULL)
3382 strcpy(hostbuf, cithost);
3383 if (portbuf != NULL)
3384 strcpy(portbuf, sockpath);
3385 strcpy(ipc->ip_hostname, "");
3386 strcpy(ipc->ip_address, "");
3390 printf("[%s:%s]\n", cithost, citport);
3391 ipc->sock = tcp_connectsock(cithost, citport);
3392 if (ipc->sock == -1) {
3398 /* Learn the actual network identity of the host to which we are connected */
3400 struct sockaddr_in6 clientaddr;
3401 unsigned int addrlen = sizeof(clientaddr);
3403 ipc->ip_hostname[0] = 0;
3404 ipc->ip_address[0] = 0;
3406 getpeername(ipc->sock, (struct sockaddr *) &clientaddr, &addrlen);
3407 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0);
3408 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST);
3410 /* stuff other things elsewhere */
3412 if (hostbuf != NULL)
3413 strcpy(hostbuf, cithost);
3414 if (portbuf != NULL)
3415 strcpy(portbuf, citport);
3421 * Disconnect and delete the IPC class (destructor)
3423 void CtdlIPC_delete(CtdlIPC * ipc)
3427 SSL_shutdown(ipc->ssl);
3432 if (ipc->sock > -1) {
3433 shutdown(ipc->sock, 2); /* Close it up */
3436 if (ipc->Buf != NULL)
3445 * Disconnect and delete the IPC class (destructor)
3446 * Also NULLs out the pointer
3448 void CtdlIPC_delete_ptr(CtdlIPC ** pipc)
3450 CtdlIPC_delete(*pipc);
3456 * return the file descriptor of the server socket so we can select() on it.
3458 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3461 int CtdlIPC_getsockfd(CtdlIPC * ipc)
3468 * return one character
3470 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3473 char CtdlIPC_get(CtdlIPC * ipc)
3478 serv_read(ipc, buf, 1);