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_ldap = atoi(buf);
841 ipc->ServInfo.newuser_disabled = atoi(buf);
844 strcpy(ipc->ServInfo.default_cal_zone, buf);
847 ipc->ServInfo.load_avg = atof(buf);
850 ipc->ServInfo.worker_avg = atof(buf);
853 ipc->ServInfo.thread_count = atoi(buf);
856 ipc->ServInfo.has_sieve = atoi(buf);
859 ipc->ServInfo.fulltext_enabled = atoi(buf);
862 strcpy(ipc->ServInfo.svn_revision, buf);
865 ipc->ServInfo.guest_logins = atoi(buf);
878 int CtdlIPCReadDirectory(CtdlIPC * ipc, char **listing, char *cret)
890 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
896 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
898 int CtdlIPCSetLastRead(CtdlIPC * ipc, long msgnum, char *cret)
907 sprintf(aaa, "SLRP %ld", msgnum);
909 sprintf(aaa, "SLRP HIGHEST");
911 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
917 int CtdlIPCInviteUserToRoom(CtdlIPC * ipc, const char *username, char *cret)
927 aaa = (char *) malloc(strlen(username) + 6);
931 sprintf(aaa, "INVT %s", username);
932 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
939 int CtdlIPCKickoutUserFromRoom(CtdlIPC * ipc, const char *username, char *cret)
949 aaa = (char *) malloc(strlen(username) + 6);
951 sprintf(aaa, "KICK %s", username);
952 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
959 int CtdlIPCGetRoomAttributes(CtdlIPC * ipc, struct ctdlroom **qret, char *cret)
968 *qret = (struct ctdlroom *) calloc(1, sizeof(struct ctdlroom));
972 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
973 if (ret / 100 == 2) {
974 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
975 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
976 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
977 qret[0]->QRflags = extract_int(cret, 3);
978 qret[0]->QRfloor = extract_int(cret, 4);
979 qret[0]->QRorder = extract_int(cret, 5);
980 qret[0]->QRdefaultview = extract_int(cret, 6);
981 qret[0]->QRflags2 = extract_int(cret, 7);
988 /* set forget to kick all users out of room */
989 int CtdlIPCSetRoomAttributes(CtdlIPC * ipc, int forget, struct ctdlroom *qret, char *cret)
999 aaa = (char *) malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) + strlen(qret->QRdirname) + 64);
1003 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
1004 qret->QRname, qret->QRpasswd, qret->QRdirname,
1005 qret->QRflags, forget, qret->QRfloor, qret->QRorder, qret->QRdefaultview, qret->QRflags2);
1006 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1013 int CtdlIPCGetRoomAide(CtdlIPC * ipc, char *cret)
1018 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
1023 int CtdlIPCSetRoomAide(CtdlIPC * ipc, const char *username, char *cret)
1033 aaa = (char *) malloc(strlen(username) + 6);
1037 sprintf(aaa, "SETA %s", username);
1038 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1045 int CtdlIPCPostMessage(CtdlIPC * ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
1056 if (mr->references) {
1057 for (ptr = mr->references; *ptr != 0; ++ptr) {
1063 snprintf(cmd, sizeof cmd,
1064 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
1065 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
1066 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL, NULL, cret);
1067 if ((flag == 0) && (subject_required != NULL)) {
1068 /* Is the server strongly recommending that the user enter a message subject? */
1069 if ((cret[3] != '\0') && (cret[4] != '\0')) {
1070 *subject_required = extract_int(&cret[4], 1);
1080 int CtdlIPCRoomInfo(CtdlIPC * ipc, char **iret, char *cret)
1091 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
1096 int CtdlIPCDeleteMessage(CtdlIPC * ipc, long msgnum, char *cret)
1105 sprintf(aaa, "DELE %ld", msgnum);
1106 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1111 int CtdlIPCMoveMessage(CtdlIPC * ipc, int copy, long msgnum, const char *destroom, char *cret)
1123 aaa = (char *) malloc(strlen(destroom) + 28);
1127 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
1128 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1135 int CtdlIPCDeleteRoom(CtdlIPC * ipc, int for_real, char *cret)
1142 sprintf(aaa, "KILL %d", for_real);
1143 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1148 int CtdlIPCCreateRoom(CtdlIPC * ipc, int for_real, const char *roomname, int type, const char *password, int floor, char *cret)
1159 aaa = (char *) malloc(strlen(roomname) + strlen(password) + 40);
1162 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type, password, floor);
1164 aaa = (char *) malloc(strlen(roomname) + 40);
1167 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type, floor);
1169 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1176 int CtdlIPCForgetRoom(CtdlIPC * ipc, char *cret)
1181 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1186 int CtdlIPCSystemMessage(CtdlIPC * ipc, const char *message, char **mret, char *cret)
1201 aaa = (char *) malloc(strlen(message) + 6);
1205 sprintf(aaa, "MESG %s", message);
1206 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1213 int CtdlIPCNextUnvalidatedUser(CtdlIPC * ipc, char *cret)
1218 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1223 int CtdlIPCGetUserRegistration(CtdlIPC * ipc, const char *username, char **rret, char *cret)
1237 aaa = (char *) malloc(strlen(username) + 6);
1239 aaa = (char *) malloc(12);
1244 sprintf(aaa, "GREG %s", username);
1246 sprintf(aaa, "GREG _SELF_");
1247 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1254 int CtdlIPCValidateUser(CtdlIPC * ipc, const char *username, int axlevel, char *cret)
1263 if (axlevel < AxDeleted || axlevel > AxAideU)
1266 aaa = (char *) malloc(strlen(username) + 17);
1270 sprintf(aaa, "VALI %s|%d", username, axlevel);
1271 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1278 int CtdlIPCSetRoomInfo(CtdlIPC * ipc, int for_real, const char *info, char *cret)
1287 sprintf(aaa, "EINF %d", for_real);
1288 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1293 int CtdlIPCUserListing(CtdlIPC * ipc, char *searchstring, char **listing, char *cret)
1308 cmd = malloc(strlen(searchstring) + 10);
1309 sprintf(cmd, "LIST %s", searchstring);
1311 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1318 int CtdlIPCSetRegistration(CtdlIPC * ipc, const char *info, char *cret)
1325 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info), NULL, NULL, cret);
1330 int CtdlIPCMiscCheck(CtdlIPC * ipc, struct ctdlipcmisc *chek, char *cret)
1339 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1340 if (ret / 100 == 2) {
1341 chek->newmail = extract_long(cret, 0);
1342 chek->needregis = extract_int(cret, 1);
1343 chek->needvalid = extract_int(cret, 2);
1350 int CtdlIPCDeleteFile(CtdlIPC * ipc, const char *filename, char *cret)
1360 aaa = (char *) malloc(strlen(filename) + 6);
1364 sprintf(aaa, "DELF %s", filename);
1365 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1372 int CtdlIPCMoveFile(CtdlIPC * ipc, const char *filename, const char *destroom, char *cret)
1384 aaa = (char *) malloc(strlen(filename) + strlen(destroom) + 7);
1388 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1389 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1396 int CtdlIPCOnlineUsers(CtdlIPC * ipc, char **listing, time_t * stamp, char *cret)
1408 *stamp = CtdlIPCServerTime(ipc, cret);
1410 *stamp = time(NULL);
1411 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1417 int CtdlIPCFileDownload(CtdlIPC * ipc, const char *filename, void **buf, size_t resume, void (*progress_gauge_callback)
1418 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1434 if (ipc->downloading)
1437 aaa = (char *) malloc(strlen(filename) + 6);
1441 sprintf(aaa, "OPEN %s", filename);
1442 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1444 if (ret / 100 == 2) {
1445 ipc->downloading = 1;
1446 bytes = extract_long(cret, 0);
1447 last_mod = extract_int(cret, 1);
1448 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1450 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret);
1452 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1453 progress_gauge_callback, cret);
1456 ret = CtdlIPCEndDownload(ipc, cret);
1458 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1465 int CtdlIPCAttachmentDownload(CtdlIPC * ipc, long msgnum, const char *part, void **buf, void (*progress_gauge_callback)
1466 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1485 if (ipc->downloading)
1488 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1489 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1490 if (ret / 100 == 2) {
1491 ipc->downloading = 1;
1492 bytes = extract_long(cret, 0);
1493 last_mod = extract_int(cret, 1);
1494 extract_token(filename, cret, 2, '|', sizeof filename);
1495 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1496 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1497 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1498 ret = CtdlIPCEndDownload(ipc, cret);
1500 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1507 int CtdlIPCImageDownload(CtdlIPC * ipc, const char *filename, void **buf, void (*progress_gauge_callback)
1508 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1524 if (ipc->downloading)
1527 aaa = (char *) malloc(strlen(filename) + 6);
1531 sprintf(aaa, "OIMG %s", filename);
1532 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1534 if (ret / 100 == 2) {
1535 ipc->downloading = 1;
1536 bytes = extract_long(cret, 0);
1537 last_mod = extract_int(cret, 1);
1538 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1539 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1540 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1541 ret = CtdlIPCEndDownload(ipc, cret);
1543 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1550 int CtdlIPCFileUpload(CtdlIPC * ipc, const char *save_as, const char *comment, const char *path, void (*progress_gauge_callback)
1551 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1556 char MimeTestBuf[64];
1557 const char *MimeType;
1573 uploadFP = fopen(path, "r");
1577 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1582 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1583 aaa = (char *) malloc(strlen(save_as) + strlen(MimeType) + strlen(comment) + 7);
1587 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1588 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1590 if (ret / 100 == 2) {
1592 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1593 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1601 int CtdlIPCImageUpload(CtdlIPC * ipc, int for_real, const char *path, const char *save_as, void (*progress_gauge_callback)
1602 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1607 char MimeTestBuf[64];
1608 const char *MimeType;
1615 if (!path && for_real)
1617 if (!*path && for_real)
1622 aaa = (char *) malloc(strlen(save_as) + 17);
1626 uploadFP = fopen(path, "r");
1630 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1634 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1636 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1637 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1639 if (ret / 100 == 2 && for_real) {
1641 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1642 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1650 int CtdlIPCQueryUsername(CtdlIPC * ipc, const char *username, char *cret)
1660 aaa = (char *) malloc(strlen(username) + 6);
1664 sprintf(aaa, "QUSR %s", username);
1665 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1672 int CtdlIPCFloorListing(CtdlIPC * ipc, char **listing, char *cret)
1683 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1688 int CtdlIPCCreateFloor(CtdlIPC * ipc, int for_real, const char *name, char *cret)
1698 sprintf(aaa, "CFLR %s|%d", name, for_real);
1699 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1705 int CtdlIPCDeleteFloor(CtdlIPC * ipc, int for_real, int floornum, char *cret)
1714 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1715 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1720 int CtdlIPCEditFloor(CtdlIPC * ipc, int floornum, const char *floorname, char *cret)
1732 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1733 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1741 * You only need to fill out hostname, the defaults will be used if any of the
1742 * other fields are not set properly.
1744 int CtdlIPCIdentifySoftware(CtdlIPC * ipc, int developerid, int clientid,
1745 int revision, const char *software_name, const char *hostname, char *cret)
1750 if (developerid < 0 || clientid < 0 || revision < 0 || !software_name) {
1753 revision = CLIENT_VERSION - 600;
1754 software_name = "Citadel (libcitadel)";
1759 aaa = (char *) malloc(strlen(software_name) + strlen(hostname) + 29);
1763 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid, revision, software_name, hostname);
1764 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1771 int CtdlIPCSendInstantMessage(CtdlIPC * ipc, const char *username, const char *text, char *cret)
1781 aaa = (char *) malloc(strlen(username) + 8);
1786 sprintf(aaa, "SEXP %s|-", username);
1787 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1789 sprintf(aaa, "SEXP %s||", username);
1790 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1798 int CtdlIPCGetInstantMessage(CtdlIPC * ipc, char **listing, char *cret)
1809 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1814 /* mode is 0 = enable, 1 = disable, 2 = status */
1815 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC * ipc, int mode, char *cret)
1822 sprintf(aaa, "DEXP %d", mode);
1823 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1828 int CtdlIPCSetBio(CtdlIPC * ipc, char *bio, char *cret)
1835 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio), NULL, NULL, cret);
1840 int CtdlIPCGetBio(CtdlIPC * ipc, const char *username, char **listing, char *cret)
1855 aaa = (char *) malloc(strlen(username) + 6);
1859 sprintf(aaa, "RBIO %s", username);
1860 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1867 int CtdlIPCListUsersWithBios(CtdlIPC * ipc, char **listing, char *cret)
1878 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1883 int CtdlIPCStealthMode(CtdlIPC * ipc, int mode, char *cret)
1890 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1891 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1896 int CtdlIPCTerminateSession(CtdlIPC * ipc, int sid, char *cret)
1903 sprintf(aaa, "TERM %d", sid);
1904 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1909 int CtdlIPCTerminateServerNow(CtdlIPC * ipc, char *cret)
1914 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1919 int CtdlIPCTerminateServerScheduled(CtdlIPC * ipc, int mode, char *cret)
1926 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1927 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1932 int CtdlIPCEnterSystemMessage(CtdlIPC * ipc, const char *filename, const char *text, char *cret)
1944 aaa = (char *) malloc(strlen(filename) + 6);
1948 sprintf(aaa, "EMSG %s", filename);
1949 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1956 /* This function returns the actual server time reported, or 0 if error */
1957 time_t CtdlIPCServerTime(CtdlIPC * ipc, char *cret)
1962 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1963 if (ret / 100 == 2) {
1964 tret = extract_long(cret, 0);
1973 int CtdlIPCAideGetUserParameters(CtdlIPC * ipc, const char *who, struct ctdluser **uret, char *cret)
1983 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
1987 sprintf(aaa, "AGUP %s", who);
1988 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1990 if (ret / 100 == 2) {
1991 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1992 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1993 uret[0]->flags = extract_int(cret, 2);
1994 uret[0]->timescalled = extract_long(cret, 3);
1995 uret[0]->posted = extract_long(cret, 4);
1996 uret[0]->axlevel = extract_int(cret, 5);
1997 uret[0]->usernum = extract_long(cret, 6);
1998 uret[0]->lastcall = extract_long(cret, 7);
1999 uret[0]->USuserpurge = extract_int(cret, 8);
2006 int CtdlIPCAideSetUserParameters(CtdlIPC * ipc, const struct ctdluser *uret, char *cret)
2016 aaa = (char *) malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
2020 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
2021 uret->fullname, uret->password, uret->flags, uret->timescalled,
2022 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge);
2023 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2030 int CtdlIPCAideGetEmailAddresses(CtdlIPC * ipc, const char *who, char *target_buf, char *cret)
2034 char *emailaddrs = NULL;
2035 size_t emailaddrs_len = 0;
2037 sprintf(aaa, "AGEA %s", who);
2038 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
2040 if (ret / 100 == 1) {
2041 strcpy(target_buf, emailaddrs);
2044 if (emailaddrs != NULL) {
2053 int CtdlIPCAideSetEmailAddresses(CtdlIPC * ipc, const char *who, char *emailaddrs, char *cret)
2065 sprintf(aaa, "ASEA %s", who);
2066 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2072 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2073 /* caller must free the struct ExpirePolicy */
2074 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC * ipc, GPEXWhichPolicy which, struct ExpirePolicy **policy, char *cret)
2076 static char *proto[] = {
2080 strof(mailboxespolicy)
2090 *policy = (struct ExpirePolicy *) calloc(1, sizeof(struct ExpirePolicy));
2093 if (which < 0 || which > 3)
2096 sprintf(cmd, "GPEX %s", proto[which]);
2097 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2098 if (ret / 100 == 2) {
2099 policy[0]->expire_mode = extract_int(cret, 0);
2100 policy[0]->expire_value = extract_int(cret, 1);
2107 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2108 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2109 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC * ipc, int which, struct ExpirePolicy *policy, char *cret)
2112 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2116 if (which < 0 || which > 3)
2120 if (policy->expire_mode < 0 || policy->expire_mode > 3)
2122 if (policy->expire_mode >= 2 && policy->expire_value < 1)
2125 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which], policy->expire_mode, policy->expire_value);
2126 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2131 int CtdlIPCGetSystemConfig(CtdlIPC * ipc, char **listing, char *cret)
2142 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0, listing, &bytes, cret);
2147 int CtdlIPCSetSystemConfig(CtdlIPC * ipc, const char *listing, char *cret)
2154 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing), NULL, NULL, cret);
2159 int CtdlIPCGetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, char **listing, char *cret)
2174 aaa = malloc(strlen(mimetype) + 13);
2177 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2178 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2185 int CtdlIPCSetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, const char *listing, char *cret)
2197 aaa = malloc(strlen(mimetype) + 13);
2200 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2201 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2208 int CtdlIPCGetRoomNetworkConfig(CtdlIPC * ipc, char **listing, char *cret)
2219 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2224 int CtdlIPCSetRoomNetworkConfig(CtdlIPC * ipc, const char *listing, char *cret)
2231 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2236 int CtdlIPCRequestClientLogout(CtdlIPC * ipc, int session, char *cret)
2245 sprintf(aaa, "REQT %d", session);
2246 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2251 int CtdlIPCSetMessageSeen(CtdlIPC * ipc, long msgnum, int seen, char *cret)
2260 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2261 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2266 int CtdlIPCStartEncryption(CtdlIPC * ipc, char *cret)
2275 /* New SSL object */
2276 temp_ssl = SSL_new(ssl_ctx);
2278 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2281 /* Pointless flag waving */
2282 #if SSLEAY_VERSION_NUMBER >= 0x0922
2283 SSL_set_session_id_context(temp_ssl, (const unsigned char *) "Citadel SID", 14);
2286 /* Associate network connection with SSL object */
2287 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2288 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2292 if (status_hook != NULL) {
2293 status_hook("Requesting encryption...\r");
2296 /* Ready to start SSL/TLS */
2297 r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2299 error_printf("Server can't start TLS: %s\n", buf);
2304 /* Do SSL/TLS handshake */
2305 if ((a = SSL_connect(temp_ssl)) < 1) {
2306 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2310 ipc->ssl = temp_ssl;
2312 error_printf("Encrypting with %s cipher %s\n",
2313 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)), SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2318 #endif /* HAVE_OPENSSL */
2323 static void endtls(SSL * ssl)
2334 int CtdlIPCDirectoryLookup(CtdlIPC * ipc, const char *address, char *cret)
2344 aaa = (char *) malloc(strlen(address) + 6);
2348 sprintf(aaa, "QDIR %s", address);
2349 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2356 int CtdlIPCInternalProgram(CtdlIPC * ipc, int secret, char *cret)
2362 sprintf(aaa, "IPGM %d", secret);
2363 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2369 /* ************************************************************************** */
2370 /* Stuff below this line is not for public consumption */
2371 /* ************************************************************************** */
2374 /* Read a listing from the server up to 000. Append to dest if it exists */
2375 char *CtdlIPCReadListing(CtdlIPC * ipc, char *dest)
2384 length = strlen(ret);
2389 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2390 linelength = strlen(aaa);
2391 ret = (char *) realloc(ret, (size_t) (length + linelength + 2));
2393 strcpy(&ret[length], aaa);
2394 length += linelength;
2395 strcpy(&ret[length++], "\n");
2403 /* Send a listing to the server; generate the ending 000. */
2404 int CtdlIPCSendListing(CtdlIPC * ipc, const char *listing)
2408 text = (char *) malloc(strlen(listing) + 6);
2410 strcpy(text, listing);
2411 while (text[strlen(text) - 1] == '\n')
2412 text[strlen(text) - 1] = '\0';
2413 strcat(text, "\n000");
2414 CtdlIPC_putline(ipc, text);
2418 /* Malloc failed but we are committed to send */
2419 /* This may result in extra blanks at the bottom */
2420 CtdlIPC_putline(ipc, text);
2421 CtdlIPC_putline(ipc, "000");
2427 /* Partial read of file from server */
2428 size_t CtdlIPCPartialRead(CtdlIPC * ipc, void **buf, size_t offset, size_t bytes, char *cret)
2441 sprintf(aaa, "READ %d|%d", (int) offset, (int) bytes);
2442 CtdlIPC_putline(ipc, aaa);
2443 CtdlIPC_getline(ipc, aaa);
2445 strcpy(cret, &aaa[4]);
2447 len = extract_long(&aaa[4], 0);
2448 *buf = (void *) realloc(*buf, (size_t) (offset + len));
2450 /* I know what I'm doing */
2451 serv_read(ipc, ((char *) (*buf) + offset), len);
2453 /* We have to read regardless */
2454 serv_read(ipc, aaa, len);
2458 CtdlIPC_unlock(ipc);
2464 int CtdlIPCEndDownload(CtdlIPC * ipc, char *cret)
2470 if (!ipc->downloading)
2473 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2475 ipc->downloading = 0;
2481 int CtdlIPCSpecifyPreferredFormats(CtdlIPC * ipc, char *cret, char *formats)
2486 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2487 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2494 int CtdlIPCReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2495 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2505 if (!ipc->downloading)
2509 if (progress_gauge_callback)
2510 progress_gauge_callback(ipc, len, bytes);
2511 while (len < bytes) {
2514 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2520 if (progress_gauge_callback)
2521 progress_gauge_callback(ipc, len, bytes);
2526 /* READ - pipelined */
2527 int CtdlIPCHighSpeedReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2528 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2531 int calls; /* How many calls in the pipeline */
2532 int i; /* iterator */
2541 if (!ipc->downloading)
2544 *buf = (void *) realloc(*buf, bytes - resume);
2550 if (progress_gauge_callback)
2551 progress_gauge_callback(ipc, len, bytes);
2553 /* How many calls will be in the pipeline? */
2554 calls = (bytes - resume) / 4096;
2555 if ((bytes - resume) % 4096)
2558 /* Send all requests at once */
2559 for (i = 0; i < calls; i++) {
2560 sprintf(aaa, "READ %d|4096", (int) (i * 4096 + resume));
2561 CtdlIPC_putline(ipc, aaa);
2564 /* Receive all responses at once */
2565 for (i = 0; i < calls; i++) {
2566 CtdlIPC_getline(ipc, aaa);
2568 strcpy(cret, &aaa[4]);
2570 len = extract_long(&aaa[4], 0);
2571 /* I know what I'm doing */
2572 serv_read(ipc, ((char *) (*buf) + (i * 4096)), len);
2574 if (progress_gauge_callback)
2575 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2577 CtdlIPC_unlock(ipc);
2583 int CtdlIPCEndUpload(CtdlIPC * ipc, int discard, char *cret)
2590 if (!ipc->uploading)
2593 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2594 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2601 int CtdlIPCWriteUpload(CtdlIPC * ipc, FILE * uploadFP, void (*progress_gauge_callback)
2602 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2609 FILE *fd = uploadFP;
2615 fseek(fd, 0L, SEEK_END);
2619 if (progress_gauge_callback)
2620 progress_gauge_callback(ipc, 0, bytes);
2622 while (offset < bytes) {
2625 /* Read some data in */
2626 to_write = fread(buf, 1, 4096, fd);
2628 if (feof(fd) || ferror(fd))
2631 sprintf(aaa, "WRIT %d", (int) to_write);
2632 CtdlIPC_putline(ipc, aaa);
2633 CtdlIPC_getline(ipc, aaa);
2634 strcpy(cret, &aaa[4]);
2636 if (aaa[0] == '7') {
2637 to_write = extract_long(&aaa[4], 0);
2639 serv_write(ipc, buf, to_write);
2641 if (progress_gauge_callback)
2642 progress_gauge_callback(ipc, offset, bytes);
2643 /* Detect short reads and back up if needed */
2644 /* offset will never be negative anyway */
2645 fseek(fd, (signed) offset, SEEK_SET);
2650 if (progress_gauge_callback)
2651 progress_gauge_callback(ipc, 1, 1);
2654 return (!ferr ? ret : -2);
2659 * Generic command method. This method should handle any server command
2660 * except for CHAT. It takes the following arguments:
2662 * ipc The server to speak with
2663 * command Preformatted command to send to server
2664 * to_send A text or binary file to send to server
2665 * (only sent if server requests it)
2666 * bytes_to_send The number of bytes in to_send (required if
2667 * sending binary, optional if sending listing)
2668 * to_receive Pointer to a NULL pointer, if the server
2669 * sends text or binary we will allocate memory
2670 * for the file and stuff it here
2671 * bytes_to_receive If a file is received, we will store its
2673 * proto_response The protocol response. Caller must provide
2674 * this buffer and ensure that it is at least
2675 * 128 bytes in length.
2677 * This function returns a number equal to the protocol response number,
2678 * -1 if an internal error occurred, -2 if caller provided bad values,
2679 * or 0 - the protocol response number if bad values were found during
2680 * the protocol exchange.
2681 * It stores the protocol response string (minus the number) in
2682 * protocol_response as described above. Some commands send additional
2683 * data in this string.
2685 int CtdlIPCGenericCommand(CtdlIPC * ipc,
2686 const char *command, const char *to_send,
2687 size_t bytes_to_send, char **to_receive, size_t * bytes_to_receive, char *proto_response)
2694 if (!proto_response)
2698 CtdlIPC_putline(ipc, command);
2700 CtdlIPC_getline(ipc, proto_response);
2701 if (proto_response[3] == '*')
2703 ret = atoi(proto_response);
2704 strcpy(proto_response, &proto_response[4]);
2705 switch (ret / 100) {
2706 default: /* Unknown, punt */
2708 case 3: /* MORE_DATA */
2710 /* Don't need to do anything */
2712 case 1: /* LISTING_FOLLOWS */
2713 if (to_receive && !*to_receive && bytes_to_receive) {
2714 *to_receive = CtdlIPCReadListing(ipc, NULL);
2715 } else { /* Drain */
2716 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2720 case 4: /* SEND_LISTING */
2722 CtdlIPCSendListing(ipc, to_send);
2724 /* No listing given, fake it */
2725 CtdlIPC_putline(ipc, "000");
2729 case 6: /* BINARY_FOLLOWS */
2730 if (to_receive && !*to_receive && bytes_to_receive) {
2731 *bytes_to_receive = extract_long(proto_response, 0);
2732 *to_receive = (char *)
2733 malloc((size_t) * bytes_to_receive);
2737 serv_read(ipc, *to_receive, *bytes_to_receive);
2743 drain = extract_long(proto_response, 0);
2744 while (drain > SIZ) {
2745 serv_read(ipc, buf, SIZ);
2748 serv_read(ipc, buf, drain);
2752 case 7: /* SEND_BINARY */
2753 if (to_send && bytes_to_send) {
2754 serv_write(ipc, to_send, bytes_to_send);
2755 } else if (bytes_to_send) {
2756 /* Fake it, send nulls */
2759 fake = bytes_to_send;
2760 memset(buf, '\0', SIZ);
2761 while (fake > SIZ) {
2762 serv_write(ipc, buf, SIZ);
2765 serv_write(ipc, buf, fake);
2767 } /* else who knows? DANGER WILL ROBINSON */
2769 case 8: /* START_CHAT_MODE */
2770 if (!strncasecmp(command, "CHAT", 4)) {
2771 /* Don't call chatmode with generic! */
2772 CtdlIPC_putline(ipc, "/quit");
2775 /* In this mode we send then receive listing */
2777 CtdlIPCSendListing(ipc, to_send);
2779 /* No listing given, fake it */
2780 CtdlIPC_putline(ipc, "000");
2783 if (to_receive && !*to_receive && bytes_to_receive) {
2784 *to_receive = CtdlIPCReadListing(ipc, NULL);
2785 } else { /* Drain */
2786 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2791 case 9: /* ASYNC_MSG */
2792 /* CtdlIPCDoAsync(ret, proto_response); */
2793 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2799 CtdlIPC_unlock(ipc);
2805 * Connect to a Citadel on a remote host using a TCP/IP socket
2807 static int tcp_connectsock(char *host, char *service)
2809 struct in6_addr serveraddr;
2810 struct addrinfo hints;
2811 struct addrinfo *res = NULL;
2812 struct addrinfo *ai = NULL;
2816 if ((host == NULL) || IsEmptyStr(host)) {
2817 service = DEFAULT_HOST;
2819 if ((service == NULL) || IsEmptyStr(service)) {
2820 service = DEFAULT_PORT;
2823 memset(&hints, 0x00, sizeof(hints));
2824 hints.ai_flags = AI_NUMERICSERV;
2825 hints.ai_family = AF_UNSPEC;
2826 hints.ai_socktype = SOCK_STREAM;
2829 * Handle numeric IPv4 and IPv6 addresses
2831 rc = inet_pton(AF_INET, host, &serveraddr);
2832 if (rc == 1) { /* dotted quad */
2833 hints.ai_family = AF_INET;
2834 hints.ai_flags |= AI_NUMERICHOST;
2836 rc = inet_pton(AF_INET6, host, &serveraddr);
2837 if (rc == 1) { /* IPv6 address */
2838 hints.ai_family = AF_INET6;
2839 hints.ai_flags |= AI_NUMERICHOST;
2843 /* Begin the connection process */
2845 rc = getaddrinfo(host, service, &hints, &res);
2851 * Try all available addresses until we connect to one or until we run out.
2853 for (ai = res; ai != NULL; ai = ai->ai_next) {
2854 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2858 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2860 return (sock); /* Connected! */
2862 close(sock); /* Failed. Close the socket to avoid fd leak! */
2874 * Connect to a Citadel on the local host using a unix domain socket
2876 static int uds_connectsock(int *isLocal, char *sockpath)
2878 struct sockaddr_un addr;
2881 memset(&addr, 0, sizeof(addr));
2882 addr.sun_family = AF_UNIX;
2883 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2885 s = socket(AF_UNIX, SOCK_STREAM, 0);
2890 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2901 * input binary data from socket
2903 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes)
2905 unsigned int len, rlen;
2907 #if defined(HAVE_OPENSSL)
2909 serv_read_ssl(ipc, buf, bytes);
2914 while (len < bytes) {
2915 rlen = read(ipc->sock, &buf[len], bytes - len);
2917 connection_died(ipc, 0);
2926 * send binary to server
2928 void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
2930 unsigned int bytes_written = 0;
2933 #if defined(HAVE_OPENSSL)
2935 serv_write_ssl(ipc, buf, nbytes);
2939 while (bytes_written < nbytes) {
2940 retval = write(ipc->sock, &buf[bytes_written], nbytes - bytes_written);
2942 connection_died(ipc, 0);
2945 bytes_written += retval;
2952 * input binary data from encrypted connection
2954 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes)
2960 while (len < bytes) {
2961 if (SSL_want_read(ipc->ssl)) {
2962 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2963 error_printf("SSL_write in serv_read:\n");
2964 ERR_print_errors_fp(stderr);
2967 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2971 errval = SSL_get_error(ipc->ssl, rlen);
2972 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
2977 Not sure why we'd want to handle these error codes any differently,
2978 but this definitely isn't the way to handle them. Someone must have
2979 naively assumed that we could fall back to unencrypted communications,
2980 but all it does is just recursively blow the stack.
2981 if (errval == SSL_ERROR_ZERO_RETURN ||
2982 errval == SSL_ERROR_SSL) {
2983 serv_read(ipc, &buf[len], bytes - len);
2987 error_printf("SSL_read in serv_read: %s\n", ERR_reason_error_string(ERR_peek_error()));
2988 connection_died(ipc, 1);
2997 * send binary to server encrypted
2999 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
3001 unsigned int bytes_written = 0;
3005 while (bytes_written < nbytes) {
3006 if (SSL_want_write(ipc->ssl)) {
3007 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
3008 error_printf("SSL_read in serv_write:\n");
3009 ERR_print_errors_fp(stderr);
3012 retval = SSL_write(ipc->ssl, &buf[bytes_written], nbytes - bytes_written);
3016 errval = SSL_get_error(ipc->ssl, retval);
3017 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
3021 if (errval == SSL_ERROR_ZERO_RETURN || errval == SSL_ERROR_SSL) {
3022 serv_write(ipc, &buf[bytes_written], nbytes - bytes_written);
3025 error_printf("SSL_write in serv_write: %s\n", ERR_reason_error_string(ERR_peek_error()));
3026 connection_died(ipc, 1);
3029 bytes_written += retval;
3036 static void CtdlIPC_init_OpenSSL(void)
3039 const SSL_METHOD *ssl_method;
3042 /* already done init */
3051 SSL_load_error_strings();
3052 SSLeay_add_ssl_algorithms();
3054 /* Set up the SSL context in which we will oeprate */
3055 ssl_method = SSLv23_client_method();
3056 ssl_ctx = SSL_CTX_new(ssl_method);
3058 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
3061 /* Any reasonable cipher we can get */
3062 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
3063 error_printf("No ciphers available for encryption\n");
3066 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
3068 /* Load DH parameters into the context */
3071 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
3075 if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
3076 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
3081 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
3085 #endif /* HAVE_OPENSSL */
3088 int ReadNetworkChunk(CtdlIPC * ipc)
3103 FD_SET(ipc->sock, &read_fd);
3104 ret = select(ipc->sock + 1, &read_fd, NULL, NULL, &tv);
3108 *(ipc->BufPtr) = '\0';
3109 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3111 ipc->BufPtr[n] = '\0';
3116 } else if (ret < 0) {
3117 if (!(errno == EINTR || errno == EAGAIN))
3118 error_printf("\nselect failed: %d %s\n", err, strerror(err));
3124 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3126 ipc->BufPtr[n]='\0';
3131 connection_died(ipc, 0);
3139 * input string from socket - implemented in terms of serv_read()
3143 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3146 char *aptr, *bptr, *aeptr, *beptr;
3148 // error_printf("---\n");
3151 #if defined(HAVE_OPENSSL)
3154 /* Read one character at a time. */
3156 serv_read(ipc, &buf[i], 1);
3157 if (buf[i] == '\n' || i == (SIZ - 1))
3161 /* If we got a long line, discard characters until the newline. */
3163 while (buf[i] != '\n')
3164 serv_read(ipc, &buf[i], 1);
3166 /* Strip the trailing newline (and carriage return, if present) */
3167 if (i >= 0 && buf[i] == 10)
3169 if (i >= 0 && buf[i] == 13)
3174 if (ipc->Buf == NULL) {
3176 ipc->Buf = (char *) malloc(ipc->BufSize + 10);
3178 ipc->BufPtr = ipc->Buf;
3182 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3183 if (ipc->BufUsed == 0)
3184 ReadNetworkChunk(ipc);
3186 //// if (ipc->BufUsed != 0) while (1)
3191 aeptr = ipc->Buf + ipc->BufSize;
3192 while ((aptr < aeptr) && (bptr < beptr) && (*aptr != '\0') && (*aptr != '\n'))
3193 *(bptr++) = *(aptr++);
3194 if ((*aptr == '\n') && (aptr < aeptr)) {
3195 /* Terminate it right, remove the line breaks */
3196 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3198 while ((aptr < aeptr) && (*(aptr + 1) == '\0'))
3201 // 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);
3202 if ((bptr > buf + 1) && (*(bptr - 1) == '\r'))
3205 /* is there more in the buffer we need to read later? */
3206 if (ipc->Buf + ipc->BufUsed > aptr) {
3210 ipc->BufPtr = ipc->Buf;
3212 // error_printf("----bla6\n");
3215 } /* should we move our read stuf to the bufferstart so we have more space at the end? */
3216 else if ((ipc->BufPtr != ipc->Buf) && (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4)))) {
3217 size_t NewBufSize = ipc->BufSize * 2;
3218 int delta = (ipc->BufPtr - ipc->Buf);
3221 /* if the line would end after our buffer, we should use a bigger buffer. */
3222 NewBuf = (char *) malloc(NewBufSize + 10);
3223 memcpy(NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3225 ipc->Buf = ipc->BufPtr = NewBuf;
3226 ipc->BufUsed -= delta;
3227 ipc->BufSize = NewBufSize;
3229 if (ReadNetworkChunk(ipc) < 0) {
3230 // error_printf("----bla\n");
3234 /// error_printf("----bl45761%s\nipc->BufUsed");
3236 // error_printf("----bla1\n");
3239 #else /* CHUNKED_READ */
3241 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3245 /* Read one character at a time. */
3247 serv_read(ipc, &buf[i], 1);
3248 if (buf[i] == '\n' || i == (SIZ - 1))
3252 /* If we got a long line, discard characters until the newline. */
3254 while (buf[i] != '\n')
3255 serv_read(ipc, &buf[i], 1);
3257 /* Strip the trailing newline (and carriage return, if present) */
3258 if (i >= 0 && buf[i] == 10)
3260 if (i >= 0 && buf[i] == 13)
3265 #endif /* CHUNKED_READ */
3268 void CtdlIPC_chat_recv(CtdlIPC * ipc, char *buf)
3270 CtdlIPC_getline(ipc, buf);
3274 * send line to server - implemented in terms of serv_write()
3276 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf)
3282 cmd = malloc(len + 2);
3284 /* This requires no extra memory */
3285 serv_write(ipc, buf, len);
3286 serv_write(ipc, "\n", 1);
3288 /* This is network-optimized */
3289 strncpy(cmd, buf, len);
3290 strcpy(cmd + len, "\n");
3291 serv_write(ipc, cmd, len + 1);
3295 ipc->last_command_sent = time(NULL);
3298 void CtdlIPC_chat_send(CtdlIPC * ipc, const char *buf)
3300 CtdlIPC_putline(ipc, buf);
3307 CtdlIPC *CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3315 ipc = malloc(sizeof(struct _CtdlIPC));
3319 #if defined(HAVE_OPENSSL)
3321 CtdlIPC_init_OpenSSL();
3323 ipc->sock = -1; /* Not connected */
3324 ipc->isLocal = 0; /* Not local, of course! */
3325 ipc->downloading = 0;
3327 ipc->last_command_sent = 0L;
3328 ipc->network_status_cb = NULL;
3333 strcpy(cithost, DEFAULT_HOST); /* default host */
3334 strcpy(citport, DEFAULT_PORT); /* default port */
3336 /* Allow caller to supply our values */
3337 if (hostbuf && strlen(hostbuf) > 0) {
3338 strcpy(cithost, hostbuf);
3340 if (portbuf && strlen(portbuf) > 0) {
3341 strcpy(citport, portbuf);
3344 /* Read host/port from command line if present */
3345 for (a = 0; a < argc; ++a) {
3348 } else if (a == 1) {
3349 strcpy(cithost, argv[a]);
3350 } else if (a == 2) {
3351 strcpy(citport, argv[a]);
3353 error_printf("%s: usage: ", argv[0]);
3354 error_printf("%s [host] [port] ", argv[0]);
3361 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3365 /* If we're using a unix domain socket we can do a bunch of stuff */
3366 if (!strcmp(cithost, UDS)) {
3367 if (!strcasecmp(citport, DEFAULT_PORT)) {
3368 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3370 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3372 printf("[%s]\n", sockpath);
3373 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3374 if (ipc->sock == -1) {
3378 if (hostbuf != NULL)
3379 strcpy(hostbuf, cithost);
3380 if (portbuf != NULL)
3381 strcpy(portbuf, sockpath);
3382 strcpy(ipc->ip_hostname, "");
3383 strcpy(ipc->ip_address, "");
3387 printf("[%s:%s]\n", cithost, citport);
3388 ipc->sock = tcp_connectsock(cithost, citport);
3389 if (ipc->sock == -1) {
3395 /* Learn the actual network identity of the host to which we are connected */
3397 struct sockaddr_in6 clientaddr;
3398 unsigned int addrlen = sizeof(clientaddr);
3400 ipc->ip_hostname[0] = 0;
3401 ipc->ip_address[0] = 0;
3403 getpeername(ipc->sock, (struct sockaddr *) &clientaddr, &addrlen);
3404 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0);
3405 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST);
3407 /* stuff other things elsewhere */
3409 if (hostbuf != NULL)
3410 strcpy(hostbuf, cithost);
3411 if (portbuf != NULL)
3412 strcpy(portbuf, citport);
3418 * Disconnect and delete the IPC class (destructor)
3420 void CtdlIPC_delete(CtdlIPC * ipc)
3424 SSL_shutdown(ipc->ssl);
3429 if (ipc->sock > -1) {
3430 shutdown(ipc->sock, 2); /* Close it up */
3433 if (ipc->Buf != NULL)
3442 * Disconnect and delete the IPC class (destructor)
3443 * Also NULLs out the pointer
3445 void CtdlIPC_delete_ptr(CtdlIPC ** pipc)
3447 CtdlIPC_delete(*pipc);
3453 * return the file descriptor of the server socket so we can select() on it.
3455 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3458 int CtdlIPC_getsockfd(CtdlIPC * ipc)
3465 * return one character
3467 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3470 char CtdlIPC_get(CtdlIPC * ipc)
3475 serv_read(ipc, buf, 1);