2 * Copyright (c) 1987-2019 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
13 #include "textclient.h"
16 static SSL_CTX *ssl_ctx;
20 #endif /* HAVE_OPENSSL */
23 #define INADDR_NONE 0xffffffff
26 static void (*status_hook) (char *s) = NULL;
27 char ctdl_autoetc_dir[PATH_MAX] = "";
28 char file_citadel_rc[PATH_MAX] = "";
29 char ctdl_run_dir[PATH_MAX] = "";
30 char ctdl_etc_dir[PATH_MAX] = "";
31 char ctdl_home_directory[PATH_MAX] = "";
32 char file_citadel_socket[PATH_MAX] = "";
54 void CtdlIPC_lock(CtdlIPC * ipc)
56 if (ipc->network_status_cb)
57 ipc->network_status_cb(1);
61 void CtdlIPC_unlock(CtdlIPC * ipc)
63 if (ipc->network_status_cb)
64 ipc->network_status_cb(0);
68 char *libcitadelclient_version_string(void)
70 return "libcitadelclient(unnumbered)";
76 #define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
77 snprintf(SUBDIR,sizeof SUBDIR, "%s%s%s%s%s%s%s", \
78 (home&!relh)?ctdl_home_directory:basedir, \
79 ((basedir!=ctdldir)&(home&!relh))?basedir:"/", \
80 ((basedir!=ctdldir)&(home&!relh))?"/":"", \
82 (relhome[0]!='\0')?"/":"",\
84 (dirbuffer[0]!='\0')?"/":"");
86 #define DBG_PRINT(A) if (dbg==1) fprintf (stderr,"%s : %s \n", #A, A)
89 void calc_dirs_n_files(int relh, int home, const char *relhome, char *ctdldir, int dbg)
91 const char *basedir = "";
92 char dirbuffer[PATH_MAX] = "";
94 StripSlashes(ctdldir, 1);
101 COMPUTE_DIRECTORY(ctdl_run_dir);
102 StripSlashes(ctdl_run_dir, 1);
105 #ifndef HAVE_AUTO_ETC_DIR
108 basedir = AUTO_ETC_DIR;
110 COMPUTE_DIRECTORY(ctdl_autoetc_dir);
111 StripSlashes(ctdl_autoetc_dir, 1);
119 COMPUTE_DIRECTORY(ctdl_etc_dir);
120 StripSlashes(ctdl_etc_dir, 1);
124 snprintf(file_citadel_rc, sizeof file_citadel_rc, "%scitadel.rc", ctdl_etc_dir);
125 StripSlashes(file_citadel_rc, 0);
127 snprintf(file_citadel_socket, sizeof file_citadel_socket, "%scitadel.socket", ctdl_run_dir);
128 StripSlashes(file_citadel_socket, 0);
130 DBG_PRINT(ctdl_run_dir);
131 DBG_PRINT(file_citadel_socket);
132 DBG_PRINT(ctdl_etc_dir);
133 DBG_PRINT(file_citadel_rc);
136 void setCryptoStatusHook(void (*hook) (char *s))
141 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC * ipc, void (*hook) (int state))
143 ipc->network_status_cb = hook;
147 char instant_msgs = 0;
150 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes);
151 static void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes);
153 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes);
154 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes);
155 static void endtls(SSL * ssl);
156 #endif /* HAVE_OPENSSL */
157 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf);
158 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf);
162 const char *svn_revision(void);
165 * Does nothing. The server should always return 200.
167 int CtdlIPCNoop(CtdlIPC * ipc)
171 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
176 * Does nothing interesting. The server should always return 200
177 * along with your string.
179 int CtdlIPCEcho(CtdlIPC * ipc, const char *arg, char *cret)
189 aaa = (char *) malloc((size_t) (strlen(arg) + 6));
193 sprintf(aaa, "ECHO %s", arg);
194 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
201 * Asks the server to close the connecction.
202 * Should always return 200.
204 int CtdlIPCQuit(CtdlIPC * ipc)
206 int ret = 221; /* Default to successful quit */
210 if (ipc->sock > -1) {
211 CtdlIPC_putline(ipc, "QUIT");
212 CtdlIPC_getline(ipc, aaa);
217 SSL_shutdown(ipc->ssl);
221 shutdown(ipc->sock, 2); /* Close connection; we're dead */
229 * Asks the server to log out. Should always return 200, even if no user
230 * was logged in. The user will not be logged in after this!
232 int CtdlIPCLogout(CtdlIPC * ipc)
238 CtdlIPC_putline(ipc, "LOUT");
239 CtdlIPC_getline(ipc, aaa);
247 * First stage of authentication - pass the username. Returns 300 if the
248 * username is able to log in, with the username correctly spelled in cret.
249 * Returns various 500 error codes if the user doesn't exist, etc.
251 int CtdlIPCTryLogin(CtdlIPC * ipc, const char *username, char *cret)
261 aaa = (char *) malloc((size_t) (strlen(username) + 6));
265 sprintf(aaa, "USER %s", username);
266 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
273 * Second stage of authentication - provide password. The server returns
274 * 200 and several arguments in cret relating to the user's account.
276 int CtdlIPCTryPassword(CtdlIPC * ipc, const char *passwd, char *cret)
286 aaa = (char *) malloc((size_t) (strlen(passwd) + 6));
290 sprintf(aaa, "PASS %s", passwd);
291 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
298 * Second stage of authentication - provide password. The server returns
299 * 200 and several arguments in cret relating to the user's account.
301 int CtdlIPCTryApopPassword(CtdlIPC * ipc, const char *response, char *cret)
311 aaa = (char *) malloc((size_t) (strlen(response) + 6));
315 sprintf(aaa, "PAS2 %s", response);
316 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
323 * Create a new user. This returns 200 plus the same arguments as TryPassword
324 * if selfservice is nonzero, unless there was a problem creating the account.
325 * If selfservice is zero, creates a new user but does not log out the existing
326 * user - intended for use by system administrators to create accounts on
327 * behalf of other users.
329 int CtdlIPCCreateUser(CtdlIPC * ipc, const char *username, int selfservice, char *cret)
339 aaa = (char *) malloc((size_t) (strlen(username) + 6));
343 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
344 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
351 * Changes the user's password. Returns 200 if changed, errors otherwise.
353 int CtdlIPCChangePassword(CtdlIPC * ipc, const char *passwd, char *cret)
363 aaa = (char *) malloc((size_t) (strlen(passwd) + 6));
367 sprintf(aaa, "SETP %s", passwd);
368 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
375 /* Caller must free the march list */
376 /* Room types are defined in enum RoomList; keep these in sync! */
377 /* floor is -1 for all, or floornum */
378 int CtdlIPCKnownRooms(CtdlIPC * ipc, enum RoomList which, int floor, struct march **listing, char *cret)
381 struct march *march = NULL;
382 static char *proto[] = { "LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
390 return -2; /* Free the listing first */
393 /* if (which < 0 || which > 4) return -2; */
395 return -2; /* Can't validate upper bound, sorry */
397 sprintf(aaa, "%s %d", proto[which], floor);
398 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
399 if (ret / 100 == 1) {
402 while (bbb && strlen(bbb)) {
405 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
407 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
408 mptr = (struct march *) malloc(sizeof(struct march));
411 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
412 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
413 mptr->march_floor = (char) extract_int(aaa, 2);
414 mptr->march_order = (char) extract_int(aaa, 3);
415 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
416 mptr->march_access = (char) extract_int(aaa, 5);
423 while (mptr2->next != NULL)
438 /* Caller must free the struct ctdluser; caller may pass an existing one */
439 int CtdlIPCGetConfig(CtdlIPC * ipc, struct ctdluser **uret, char *cret)
448 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
452 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
453 if (ret / 100 == 2) {
454 uret[0]->flags = extract_int(cret, 2);
461 int CtdlIPCSetConfig(CtdlIPC * ipc, struct ctdluser *uret, char *cret)
470 sprintf(aaa, "SETU 80|24|%d", uret->flags);
471 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
476 int CtdlIPCRenameUser(CtdlIPC * ipc, char *oldname, char *newname, char *cret)
488 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
489 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
495 int CtdlIPCGotoRoom(CtdlIPC * ipc, const char *room, const char *passwd, struct ctdlipcroom **rret, char *cret)
505 *rret = (struct ctdlipcroom *) calloc(1, sizeof(struct ctdlipcroom));
510 aaa = (char *) malloc(strlen(room) + strlen(passwd) + 7);
515 sprintf(aaa, "GOTO %s|%s", room, passwd);
517 aaa = (char *) malloc(strlen(room) + 6);
522 sprintf(aaa, "GOTO %s", room);
524 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
525 if (ret / 100 == 2) {
526 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
527 rret[0]->RRunread = extract_long(cret, 1);
528 rret[0]->RRtotal = extract_long(cret, 2);
529 rret[0]->RRinfoupdated = extract_int(cret, 3);
530 rret[0]->RRflags = extract_int(cret, 4);
531 rret[0]->RRhighest = extract_long(cret, 5);
532 rret[0]->RRlastread = extract_long(cret, 6);
533 rret[0]->RRismailbox = extract_int(cret, 7);
534 rret[0]->RRaide = extract_int(cret, 8);
535 rret[0]->RRnewmail = extract_long(cret, 9);
536 rret[0]->RRfloor = extract_int(cret, 10);
537 rret[0]->RRcurrentview = extract_int(cret, 11);
538 rret[0]->RRdefaultview = extract_int(cret, 12);
539 /* position 13 is a trash folder flag ... irrelevant in this client */
540 rret[0]->RRflags2 = extract_int(cret, 14);
551 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
552 /* whicharg is number of messages, applies to last, first, gt, lt */
553 int CtdlIPCGetMessages(CtdlIPC * ipc, enum MessageList which, int whicharg, const char *mtemplate, unsigned long **mret, char *cret)
556 unsigned long count = 0;
557 static char *proto[] = { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
568 if (which < 0 || which > 6)
572 sprintf(aaa, "MSGS %s||%d", proto[which], (mtemplate) ? 1 : 0);
574 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg, (mtemplate) ? 1 : 0);
576 count = strlen(mtemplate);
577 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
581 *mret = (unsigned long *) calloc(1, sizeof(unsigned long));
584 while (bbb && strlen(bbb)) {
585 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
586 remove_token(bbb, 0, '\n');
587 *mret = (unsigned long *) realloc(*mret, (size_t) ((count + 2) * sizeof(unsigned long)));
589 (*mret)[count++] = atol(aaa);
602 int CtdlIPCGetSingleMessage(CtdlIPC * ipc, long msgnum, int headers, int as_mime, struct ctdlipcmessage **mret, char *cret)
608 int multipart_hunting = 0;
609 char multipart_prefix[128];
617 *mret = (struct ctdlipcmessage *) calloc(1, sizeof(struct ctdlipcmessage));
623 strcpy(encoding, "");
624 strcpy(mret[0]->content_type, "");
625 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
626 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
627 if (ret / 100 == 1) {
629 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
630 while (strlen(bbb) > 4 && bbb[4] == '=') {
631 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
632 remove_token(bbb, 0, '\n');
634 if (!strncasecmp(aaa, "nhdr=yes", 8))
636 else if (!strncasecmp(aaa, "from=", 5))
637 safestrncpy(mret[0]->author, &aaa[5], SIZ);
638 else if (!strncasecmp(aaa, "type=", 5))
639 mret[0]->type = atoi(&aaa[5]);
640 else if (!strncasecmp(aaa, "msgn=", 5))
641 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
642 else if (!strncasecmp(aaa, "subj=", 5))
643 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
644 else if (!strncasecmp(aaa, "rfca=", 5))
645 safestrncpy(mret[0]->email, &aaa[5], SIZ);
646 else if (!strncasecmp(aaa, "room=", 5))
647 safestrncpy(mret[0]->room, &aaa[5], SIZ);
648 else if (!strncasecmp(aaa, "rcpt=", 5))
649 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
650 else if (!strncasecmp(aaa, "wefw=", 5))
651 safestrncpy(mret[0]->references, &aaa[5], SIZ);
652 else if (!strncasecmp(aaa, "time=", 5))
653 mret[0]->time = atol(&aaa[5]);
655 /* Multipart/alternative prefix & suffix strings help
656 * us to determine which part we want to download.
658 else if (!strncasecmp(aaa, "pref=", 5)) {
659 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
660 if (!strcasecmp(multipart_prefix, "multipart/alternative")) {
663 } else if (!strncasecmp(aaa, "suff=", 5)) {
664 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
665 if (!strcasecmp(multipart_prefix, "multipart/alternative")) {
670 else if (!strncasecmp(aaa, "part=", 5)) {
671 struct parts *ptr, *chain;
673 ptr = (struct parts *) calloc(1, sizeof(struct parts));
676 /* Fill the buffers for the caller */
677 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
678 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
679 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
680 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
681 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
682 ptr->length = extract_long(&aaa[5], 5);
683 if (!mret[0]->attachments)
684 mret[0]->attachments = ptr;
686 chain = mret[0]->attachments;
692 /* Now handle multipart/alternative */
693 if (multipart_hunting > 0) {
694 if ((!strcasecmp(ptr->mimetype, "text/plain"))
695 || (!strcasecmp(ptr->mimetype, "text/html"))) {
696 strcpy(mret[0]->mime_chosen, ptr->number);
703 /* Eliminate "text\n" */
704 remove_token(bbb, 0, '\n');
706 /* If doing a MIME thing, pull out the extra headers */
709 if (!strncasecmp(bbb, "Content-type:", 13)) {
710 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
711 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
712 striplt(mret[0]->content_type);
714 /* strip out ";charset=" portion. FIXME do something with
715 * the charset (like... convert it) instead of just throwing
718 if (strstr(mret[0]->content_type, ";") != NULL) {
719 strcpy(strstr(mret[0]->content_type, ";"), "");
723 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
724 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
725 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
726 striplt(mret[0]->mime_chosen);
728 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
729 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
730 strcpy(encoding, &encoding[26]);
733 remove_token(bbb, 0, '\n');
734 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
735 remove_token(bbb, 0, '\n');
742 if ((!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable"))) {
744 int bytes_decoded = 0;
745 ccc = malloc(strlen(bbb) + 32768);
746 if (!strcasecmp(encoding, "base64")) {
747 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
748 } else if (!strcasecmp(encoding, "quoted-printable")) {
749 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
751 ccc[bytes_decoded] = 0;
756 /* FIXME: Strip trailing whitespace */
757 bbb = (char *) realloc(bbb, (size_t) (strlen(bbb) + 1));
760 bbb = (char *) realloc(bbb, 1);
770 int CtdlIPCWhoKnowsRoom(CtdlIPC * ipc, char **listing, char *cret)
782 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
788 int CtdlIPCServerInfo(CtdlIPC * ipc, char *cret)
792 char *listing = NULL;
798 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
799 if (ret / 100 == 1) {
802 while (*listing && strlen(listing)) {
803 extract_token(buf, listing, 0, '\n', sizeof buf);
804 remove_token(listing, 0, '\n');
807 ipc->ServInfo.pid = atoi(buf);
810 strcpy(ipc->ServInfo.nodename, buf);
813 strcpy(ipc->ServInfo.humannode, buf);
816 strcpy(ipc->ServInfo.fqdn, buf);
819 strcpy(ipc->ServInfo.software, buf);
822 ipc->ServInfo.rev_level = atoi(buf);
825 strcpy(ipc->ServInfo.site_location, buf);
828 strcpy(ipc->ServInfo.sysadm, buf);
831 strcpy(ipc->ServInfo.moreprompt, buf);
834 ipc->ServInfo.ok_floors = atoi(buf);
837 ipc->ServInfo.paging_level = atoi(buf);
840 ipc->ServInfo.supports_qnop = atoi(buf);
843 ipc->ServInfo.supports_ldap = atoi(buf);
846 ipc->ServInfo.newuser_disabled = atoi(buf);
849 strcpy(ipc->ServInfo.default_cal_zone, buf);
852 ipc->ServInfo.load_avg = atof(buf);
855 ipc->ServInfo.worker_avg = atof(buf);
858 ipc->ServInfo.thread_count = atoi(buf);
861 ipc->ServInfo.has_sieve = atoi(buf);
864 ipc->ServInfo.fulltext_enabled = atoi(buf);
867 strcpy(ipc->ServInfo.svn_revision, buf);
870 ipc->ServInfo.guest_logins = atoi(buf);
883 int CtdlIPCReadDirectory(CtdlIPC * ipc, char **listing, char *cret)
895 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
901 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
903 int CtdlIPCSetLastRead(CtdlIPC * ipc, long msgnum, char *cret)
912 sprintf(aaa, "SLRP %ld", msgnum);
914 sprintf(aaa, "SLRP HIGHEST");
916 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
922 int CtdlIPCInviteUserToRoom(CtdlIPC * ipc, const char *username, char *cret)
932 aaa = (char *) malloc(strlen(username) + 6);
936 sprintf(aaa, "INVT %s", username);
937 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
944 int CtdlIPCKickoutUserFromRoom(CtdlIPC * ipc, const char *username, char *cret)
954 aaa = (char *) malloc(strlen(username) + 6);
956 sprintf(aaa, "KICK %s", username);
957 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
964 int CtdlIPCGetRoomAttributes(CtdlIPC * ipc, struct ctdlroom **qret, char *cret)
973 *qret = (struct ctdlroom *) calloc(1, sizeof(struct ctdlroom));
977 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
978 if (ret / 100 == 2) {
979 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
980 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
981 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
982 qret[0]->QRflags = extract_int(cret, 3);
983 qret[0]->QRfloor = extract_int(cret, 4);
984 qret[0]->QRorder = extract_int(cret, 5);
985 qret[0]->QRdefaultview = extract_int(cret, 6);
986 qret[0]->QRflags2 = extract_int(cret, 7);
993 /* set forget to kick all users out of room */
994 int CtdlIPCSetRoomAttributes(CtdlIPC * ipc, int forget, struct ctdlroom *qret, char *cret)
1004 aaa = (char *) malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) + strlen(qret->QRdirname) + 64);
1008 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
1009 qret->QRname, qret->QRpasswd, qret->QRdirname,
1010 qret->QRflags, forget, qret->QRfloor, qret->QRorder, qret->QRdefaultview, qret->QRflags2);
1011 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1018 int CtdlIPCGetRoomAide(CtdlIPC * ipc, char *cret)
1023 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
1028 int CtdlIPCSetRoomAide(CtdlIPC * ipc, const char *username, char *cret)
1038 aaa = (char *) malloc(strlen(username) + 6);
1042 sprintf(aaa, "SETA %s", username);
1043 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1050 int CtdlIPCPostMessage(CtdlIPC * ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
1061 if (mr->references) {
1062 for (ptr = mr->references; *ptr != 0; ++ptr) {
1068 snprintf(cmd, sizeof cmd,
1069 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
1070 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
1071 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL, NULL, cret);
1072 if ((flag == 0) && (subject_required != NULL)) {
1073 /* Is the server strongly recommending that the user enter a message subject? */
1074 if ((cret[3] != '\0') && (cret[4] != '\0')) {
1075 *subject_required = extract_int(&cret[4], 1);
1085 int CtdlIPCRoomInfo(CtdlIPC * ipc, char **iret, char *cret)
1096 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
1101 int CtdlIPCDeleteMessage(CtdlIPC * ipc, long msgnum, char *cret)
1110 sprintf(aaa, "DELE %ld", msgnum);
1111 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1116 int CtdlIPCMoveMessage(CtdlIPC * ipc, int copy, long msgnum, const char *destroom, char *cret)
1128 aaa = (char *) malloc(strlen(destroom) + 28);
1132 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
1133 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1140 int CtdlIPCDeleteRoom(CtdlIPC * ipc, int for_real, char *cret)
1147 sprintf(aaa, "KILL %d", for_real);
1148 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1153 int CtdlIPCCreateRoom(CtdlIPC * ipc, int for_real, const char *roomname, int type, const char *password, int floor, char *cret)
1164 aaa = (char *) malloc(strlen(roomname) + strlen(password) + 40);
1167 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type, password, floor);
1169 aaa = (char *) malloc(strlen(roomname) + 40);
1172 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type, floor);
1174 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1181 int CtdlIPCForgetRoom(CtdlIPC * ipc, char *cret)
1186 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1191 int CtdlIPCSystemMessage(CtdlIPC * ipc, const char *message, char **mret, char *cret)
1206 aaa = (char *) malloc(strlen(message) + 6);
1210 sprintf(aaa, "MESG %s", message);
1211 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1218 int CtdlIPCNextUnvalidatedUser(CtdlIPC * ipc, char *cret)
1223 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1228 int CtdlIPCGetUserRegistration(CtdlIPC * ipc, const char *username, char **rret, char *cret)
1242 aaa = (char *) malloc(strlen(username) + 6);
1244 aaa = (char *) malloc(12);
1249 sprintf(aaa, "GREG %s", username);
1251 sprintf(aaa, "GREG _SELF_");
1252 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1259 int CtdlIPCValidateUser(CtdlIPC * ipc, const char *username, int axlevel, char *cret)
1268 if (axlevel < AxDeleted || axlevel > AxAideU)
1271 aaa = (char *) malloc(strlen(username) + 17);
1275 sprintf(aaa, "VALI %s|%d", username, axlevel);
1276 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1283 int CtdlIPCSetRoomInfo(CtdlIPC * ipc, int for_real, const char *info, char *cret)
1292 sprintf(aaa, "EINF %d", for_real);
1293 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1298 int CtdlIPCUserListing(CtdlIPC * ipc, char *searchstring, char **listing, char *cret)
1313 cmd = malloc(strlen(searchstring) + 10);
1314 sprintf(cmd, "LIST %s", searchstring);
1316 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1323 int CtdlIPCSetRegistration(CtdlIPC * ipc, const char *info, char *cret)
1330 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info), NULL, NULL, cret);
1335 int CtdlIPCMiscCheck(CtdlIPC * ipc, struct ctdlipcmisc *chek, char *cret)
1344 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1345 if (ret / 100 == 2) {
1346 chek->newmail = extract_long(cret, 0);
1347 chek->needregis = extract_int(cret, 1);
1348 chek->needvalid = extract_int(cret, 2);
1355 int CtdlIPCDeleteFile(CtdlIPC * ipc, const char *filename, char *cret)
1365 aaa = (char *) malloc(strlen(filename) + 6);
1369 sprintf(aaa, "DELF %s", filename);
1370 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1377 int CtdlIPCMoveFile(CtdlIPC * ipc, const char *filename, const char *destroom, char *cret)
1389 aaa = (char *) malloc(strlen(filename) + strlen(destroom) + 7);
1393 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1394 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1401 int CtdlIPCOnlineUsers(CtdlIPC * ipc, char **listing, time_t * stamp, char *cret)
1413 *stamp = CtdlIPCServerTime(ipc, cret);
1415 *stamp = time(NULL);
1416 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1422 int CtdlIPCFileDownload(CtdlIPC * ipc, const char *filename, void **buf, size_t resume, void (*progress_gauge_callback)
1423 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1439 if (ipc->downloading)
1442 aaa = (char *) malloc(strlen(filename) + 6);
1446 sprintf(aaa, "OPEN %s", filename);
1447 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1449 if (ret / 100 == 2) {
1450 ipc->downloading = 1;
1451 bytes = extract_long(cret, 0);
1452 last_mod = extract_int(cret, 1);
1453 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1455 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret);
1457 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1458 progress_gauge_callback, cret);
1461 ret = CtdlIPCEndDownload(ipc, cret);
1463 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1470 int CtdlIPCAttachmentDownload(CtdlIPC * ipc, long msgnum, const char *part, void **buf, void (*progress_gauge_callback)
1471 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1490 if (ipc->downloading)
1493 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1494 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1495 if (ret / 100 == 2) {
1496 ipc->downloading = 1;
1497 bytes = extract_long(cret, 0);
1498 last_mod = extract_int(cret, 1);
1499 extract_token(filename, cret, 2, '|', sizeof filename);
1500 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1501 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1502 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1503 ret = CtdlIPCEndDownload(ipc, cret);
1505 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1512 int CtdlIPCImageDownload(CtdlIPC * ipc, const char *filename, void **buf, void (*progress_gauge_callback)
1513 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1529 if (ipc->downloading)
1532 aaa = (char *) malloc(strlen(filename) + 6);
1536 sprintf(aaa, "OIMG %s", filename);
1537 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1539 if (ret / 100 == 2) {
1540 ipc->downloading = 1;
1541 bytes = extract_long(cret, 0);
1542 last_mod = extract_int(cret, 1);
1543 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1544 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1545 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1546 ret = CtdlIPCEndDownload(ipc, cret);
1548 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1555 int CtdlIPCFileUpload(CtdlIPC * ipc, const char *save_as, const char *comment, const char *path, void (*progress_gauge_callback)
1556 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1561 char MimeTestBuf[64];
1562 const char *MimeType;
1578 uploadFP = fopen(path, "r");
1582 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1587 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1588 aaa = (char *) malloc(strlen(save_as) + strlen(MimeType) + strlen(comment) + 7);
1592 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1593 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1595 if (ret / 100 == 2) {
1597 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1598 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1606 int CtdlIPCImageUpload(CtdlIPC * ipc, int for_real, const char *path, const char *save_as, void (*progress_gauge_callback)
1607 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1612 char MimeTestBuf[64];
1613 const char *MimeType;
1620 if (!path && for_real)
1622 if (!*path && for_real)
1627 aaa = (char *) malloc(strlen(save_as) + 17);
1631 uploadFP = fopen(path, "r");
1635 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1639 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1641 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1642 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1644 if (ret / 100 == 2 && for_real) {
1646 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1647 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1655 int CtdlIPCQueryUsername(CtdlIPC * ipc, const char *username, char *cret)
1665 aaa = (char *) malloc(strlen(username) + 6);
1669 sprintf(aaa, "QUSR %s", username);
1670 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1677 int CtdlIPCFloorListing(CtdlIPC * ipc, char **listing, char *cret)
1688 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1693 int CtdlIPCCreateFloor(CtdlIPC * ipc, int for_real, const char *name, char *cret)
1703 sprintf(aaa, "CFLR %s|%d", name, for_real);
1704 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1710 int CtdlIPCDeleteFloor(CtdlIPC * ipc, int for_real, int floornum, char *cret)
1719 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1720 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1725 int CtdlIPCEditFloor(CtdlIPC * ipc, int floornum, const char *floorname, char *cret)
1737 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1738 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1746 * You only need to fill out hostname, the defaults will be used if any of the
1747 * other fields are not set properly.
1749 int CtdlIPCIdentifySoftware(CtdlIPC * ipc, int developerid, int clientid,
1750 int revision, const char *software_name, const char *hostname, char *cret)
1755 if (developerid < 0 || clientid < 0 || revision < 0 || !software_name) {
1758 revision = CLIENT_VERSION - 600;
1759 software_name = "Citadel (libcitadel)";
1764 aaa = (char *) malloc(strlen(software_name) + strlen(hostname) + 29);
1768 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid, revision, software_name, hostname);
1769 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1776 int CtdlIPCSendInstantMessage(CtdlIPC * ipc, const char *username, const char *text, char *cret)
1786 aaa = (char *) malloc(strlen(username) + 8);
1791 sprintf(aaa, "SEXP %s|-", username);
1792 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1794 sprintf(aaa, "SEXP %s||", username);
1795 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1803 int CtdlIPCGetInstantMessage(CtdlIPC * ipc, char **listing, char *cret)
1814 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1819 /* mode is 0 = enable, 1 = disable, 2 = status */
1820 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC * ipc, int mode, char *cret)
1827 sprintf(aaa, "DEXP %d", mode);
1828 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1833 int CtdlIPCSetBio(CtdlIPC * ipc, char *bio, char *cret)
1840 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio), NULL, NULL, cret);
1845 int CtdlIPCGetBio(CtdlIPC * ipc, const char *username, char **listing, char *cret)
1860 aaa = (char *) malloc(strlen(username) + 6);
1864 sprintf(aaa, "RBIO %s", username);
1865 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1872 int CtdlIPCListUsersWithBios(CtdlIPC * ipc, char **listing, char *cret)
1883 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1888 int CtdlIPCStealthMode(CtdlIPC * ipc, int mode, char *cret)
1895 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1896 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1901 int CtdlIPCTerminateSession(CtdlIPC * ipc, int sid, char *cret)
1908 sprintf(aaa, "TERM %d", sid);
1909 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1914 int CtdlIPCTerminateServerNow(CtdlIPC * ipc, char *cret)
1919 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1924 int CtdlIPCTerminateServerScheduled(CtdlIPC * ipc, int mode, char *cret)
1931 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1932 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1937 int CtdlIPCEnterSystemMessage(CtdlIPC * ipc, const char *filename, const char *text, char *cret)
1949 aaa = (char *) malloc(strlen(filename) + 6);
1953 sprintf(aaa, "EMSG %s", filename);
1954 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1961 /* This function returns the actual server time reported, or 0 if error */
1962 time_t CtdlIPCServerTime(CtdlIPC * ipc, char *cret)
1967 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1968 if (ret / 100 == 2) {
1969 tret = extract_long(cret, 0);
1978 int CtdlIPCAideGetUserParameters(CtdlIPC * ipc, const char *who, struct ctdluser **uret, char *cret)
1988 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
1992 sprintf(aaa, "AGUP %s", who);
1993 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1995 if (ret / 100 == 2) {
1996 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1997 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1998 uret[0]->flags = extract_int(cret, 2);
1999 uret[0]->timescalled = extract_long(cret, 3);
2000 uret[0]->posted = extract_long(cret, 4);
2001 uret[0]->axlevel = extract_int(cret, 5);
2002 uret[0]->usernum = extract_long(cret, 6);
2003 uret[0]->lastcall = extract_long(cret, 7);
2004 uret[0]->USuserpurge = extract_int(cret, 8);
2011 int CtdlIPCAideSetUserParameters(CtdlIPC * ipc, const struct ctdluser *uret, char *cret)
2021 aaa = (char *) malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
2025 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
2026 uret->fullname, uret->password, uret->flags, uret->timescalled,
2027 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge);
2028 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2035 int CtdlIPCAideGetEmailAddresses(CtdlIPC * ipc, const char *who, char *target_buf, char *cret)
2039 char *emailaddrs = NULL;
2040 size_t emailaddrs_len = 0;
2042 sprintf(aaa, "AGEA %s", who);
2043 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
2045 if (ret / 100 == 1) {
2046 strcpy(target_buf, emailaddrs);
2049 if (emailaddrs != NULL) {
2058 int CtdlIPCAideSetEmailAddresses(CtdlIPC * ipc, const char *who, char *emailaddrs, char *cret)
2070 sprintf(aaa, "ASEA %s", who);
2071 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2077 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2078 /* caller must free the struct ExpirePolicy */
2079 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC * ipc, GPEXWhichPolicy which, struct ExpirePolicy **policy, char *cret)
2081 static char *proto[] = {
2085 strof(mailboxespolicy)
2095 *policy = (struct ExpirePolicy *) calloc(1, sizeof(struct ExpirePolicy));
2098 if (which < 0 || which > 3)
2101 sprintf(cmd, "GPEX %s", proto[which]);
2102 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2103 if (ret / 100 == 2) {
2104 policy[0]->expire_mode = extract_int(cret, 0);
2105 policy[0]->expire_value = extract_int(cret, 1);
2112 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2113 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2114 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC * ipc, int which, struct ExpirePolicy *policy, char *cret)
2117 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2121 if (which < 0 || which > 3)
2125 if (policy->expire_mode < 0 || policy->expire_mode > 3)
2127 if (policy->expire_mode >= 2 && policy->expire_value < 1)
2130 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which], policy->expire_mode, policy->expire_value);
2131 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2136 int CtdlIPCGetSystemConfig(CtdlIPC * ipc, char **listing, char *cret)
2147 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0, listing, &bytes, cret);
2152 int CtdlIPCSetSystemConfig(CtdlIPC * ipc, const char *listing, char *cret)
2159 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing), NULL, NULL, cret);
2164 int CtdlIPCGetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, char **listing, char *cret)
2179 aaa = malloc(strlen(mimetype) + 13);
2182 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2183 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2190 int CtdlIPCSetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, const char *listing, char *cret)
2202 aaa = malloc(strlen(mimetype) + 13);
2205 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2206 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2213 int CtdlIPCGetRoomNetworkConfig(CtdlIPC * ipc, char **listing, char *cret)
2224 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2229 int CtdlIPCSetRoomNetworkConfig(CtdlIPC * ipc, const char *listing, char *cret)
2236 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2241 int CtdlIPCRequestClientLogout(CtdlIPC * ipc, int session, char *cret)
2250 sprintf(aaa, "REQT %d", session);
2251 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2256 int CtdlIPCSetMessageSeen(CtdlIPC * ipc, long msgnum, int seen, char *cret)
2265 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2266 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2271 int CtdlIPCStartEncryption(CtdlIPC * ipc, char *cret)
2280 /* New SSL object */
2281 temp_ssl = SSL_new(ssl_ctx);
2283 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2286 /* Pointless flag waving */
2287 #if SSLEAY_VERSION_NUMBER >= 0x0922
2288 SSL_set_session_id_context(temp_ssl, (const unsigned char *) "Citadel SID", 14);
2291 /* Associate network connection with SSL object */
2292 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2293 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2297 if (status_hook != NULL) {
2298 status_hook("Requesting encryption...\r");
2301 /* Ready to start SSL/TLS */
2302 r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2304 error_printf("Server can't start TLS: %s\n", buf);
2309 /* Do SSL/TLS handshake */
2310 if ((a = SSL_connect(temp_ssl)) < 1) {
2311 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2315 ipc->ssl = temp_ssl;
2317 error_printf("Encrypting with %s cipher %s\n",
2318 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)), SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2323 #endif /* HAVE_OPENSSL */
2328 static void endtls(SSL * ssl)
2339 int CtdlIPCDirectoryLookup(CtdlIPC * ipc, const char *address, char *cret)
2349 aaa = (char *) malloc(strlen(address) + 6);
2353 sprintf(aaa, "QDIR %s", address);
2354 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2361 int CtdlIPCInternalProgram(CtdlIPC * ipc, int secret, char *cret)
2367 sprintf(aaa, "IPGM %d", secret);
2368 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2374 /* ************************************************************************** */
2375 /* Stuff below this line is not for public consumption */
2376 /* ************************************************************************** */
2379 /* Read a listing from the server up to 000. Append to dest if it exists */
2380 char *CtdlIPCReadListing(CtdlIPC * ipc, char *dest)
2389 length = strlen(ret);
2394 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2395 linelength = strlen(aaa);
2396 ret = (char *) realloc(ret, (size_t) (length + linelength + 2));
2398 strcpy(&ret[length], aaa);
2399 length += linelength;
2400 strcpy(&ret[length++], "\n");
2408 /* Send a listing to the server; generate the ending 000. */
2409 int CtdlIPCSendListing(CtdlIPC * ipc, const char *listing)
2413 text = (char *) malloc(strlen(listing) + 6);
2415 strcpy(text, listing);
2416 while (text[strlen(text) - 1] == '\n')
2417 text[strlen(text) - 1] = '\0';
2418 strcat(text, "\n000");
2419 CtdlIPC_putline(ipc, text);
2423 /* Malloc failed but we are committed to send */
2424 /* This may result in extra blanks at the bottom */
2425 CtdlIPC_putline(ipc, text);
2426 CtdlIPC_putline(ipc, "000");
2432 /* Partial read of file from server */
2433 size_t CtdlIPCPartialRead(CtdlIPC * ipc, void **buf, size_t offset, size_t bytes, char *cret)
2446 sprintf(aaa, "READ %d|%d", (int) offset, (int) bytes);
2447 CtdlIPC_putline(ipc, aaa);
2448 CtdlIPC_getline(ipc, aaa);
2450 strcpy(cret, &aaa[4]);
2452 len = extract_long(&aaa[4], 0);
2453 *buf = (void *) realloc(*buf, (size_t) (offset + len));
2455 /* I know what I'm doing */
2456 serv_read(ipc, ((char *) (*buf) + offset), len);
2458 /* We have to read regardless */
2459 serv_read(ipc, aaa, len);
2463 CtdlIPC_unlock(ipc);
2469 int CtdlIPCEndDownload(CtdlIPC * ipc, char *cret)
2475 if (!ipc->downloading)
2478 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2480 ipc->downloading = 0;
2486 int CtdlIPCSpecifyPreferredFormats(CtdlIPC * ipc, char *cret, char *formats)
2491 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2492 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2499 int CtdlIPCReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2500 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2510 if (!ipc->downloading)
2514 if (progress_gauge_callback)
2515 progress_gauge_callback(ipc, len, bytes);
2516 while (len < bytes) {
2519 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2525 if (progress_gauge_callback)
2526 progress_gauge_callback(ipc, len, bytes);
2531 /* READ - pipelined */
2532 int CtdlIPCHighSpeedReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2533 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2536 int calls; /* How many calls in the pipeline */
2537 int i; /* iterator */
2546 if (!ipc->downloading)
2549 *buf = (void *) realloc(*buf, bytes - resume);
2555 if (progress_gauge_callback)
2556 progress_gauge_callback(ipc, len, bytes);
2558 /* How many calls will be in the pipeline? */
2559 calls = (bytes - resume) / 4096;
2560 if ((bytes - resume) % 4096)
2563 /* Send all requests at once */
2564 for (i = 0; i < calls; i++) {
2565 sprintf(aaa, "READ %d|4096", (int) (i * 4096 + resume));
2566 CtdlIPC_putline(ipc, aaa);
2569 /* Receive all responses at once */
2570 for (i = 0; i < calls; i++) {
2571 CtdlIPC_getline(ipc, aaa);
2573 strcpy(cret, &aaa[4]);
2575 len = extract_long(&aaa[4], 0);
2576 /* I know what I'm doing */
2577 serv_read(ipc, ((char *) (*buf) + (i * 4096)), len);
2579 if (progress_gauge_callback)
2580 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2582 CtdlIPC_unlock(ipc);
2588 int CtdlIPCEndUpload(CtdlIPC * ipc, int discard, char *cret)
2595 if (!ipc->uploading)
2598 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2599 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2606 int CtdlIPCWriteUpload(CtdlIPC * ipc, FILE * uploadFP, void (*progress_gauge_callback)
2607 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2614 FILE *fd = uploadFP;
2620 fseek(fd, 0L, SEEK_END);
2624 if (progress_gauge_callback)
2625 progress_gauge_callback(ipc, 0, bytes);
2627 while (offset < bytes) {
2630 /* Read some data in */
2631 to_write = fread(buf, 1, 4096, fd);
2633 if (feof(fd) || ferror(fd))
2636 sprintf(aaa, "WRIT %d", (int) to_write);
2637 CtdlIPC_putline(ipc, aaa);
2638 CtdlIPC_getline(ipc, aaa);
2639 strcpy(cret, &aaa[4]);
2641 if (aaa[0] == '7') {
2642 to_write = extract_long(&aaa[4], 0);
2644 serv_write(ipc, buf, to_write);
2646 if (progress_gauge_callback)
2647 progress_gauge_callback(ipc, offset, bytes);
2648 /* Detect short reads and back up if needed */
2649 /* offset will never be negative anyway */
2650 fseek(fd, (signed) offset, SEEK_SET);
2655 if (progress_gauge_callback)
2656 progress_gauge_callback(ipc, 1, 1);
2659 return (!ferr ? ret : -2);
2664 * Generic command method. This method should handle any server command
2665 * except for CHAT. It takes the following arguments:
2667 * ipc The server to speak with
2668 * command Preformatted command to send to server
2669 * to_send A text or binary file to send to server
2670 * (only sent if server requests it)
2671 * bytes_to_send The number of bytes in to_send (required if
2672 * sending binary, optional if sending listing)
2673 * to_receive Pointer to a NULL pointer, if the server
2674 * sends text or binary we will allocate memory
2675 * for the file and stuff it here
2676 * bytes_to_receive If a file is received, we will store its
2678 * proto_response The protocol response. Caller must provide
2679 * this buffer and ensure that it is at least
2680 * 128 bytes in length.
2682 * This function returns a number equal to the protocol response number,
2683 * -1 if an internal error occurred, -2 if caller provided bad values,
2684 * or 0 - the protocol response number if bad values were found during
2685 * the protocol exchange.
2686 * It stores the protocol response string (minus the number) in
2687 * protocol_response as described above. Some commands send additional
2688 * data in this string.
2690 int CtdlIPCGenericCommand(CtdlIPC * ipc,
2691 const char *command, const char *to_send,
2692 size_t bytes_to_send, char **to_receive, size_t * bytes_to_receive, char *proto_response)
2699 if (!proto_response)
2703 CtdlIPC_putline(ipc, command);
2705 CtdlIPC_getline(ipc, proto_response);
2706 if (proto_response[3] == '*')
2708 ret = atoi(proto_response);
2709 strcpy(proto_response, &proto_response[4]);
2710 switch (ret / 100) {
2711 default: /* Unknown, punt */
2713 case 3: /* MORE_DATA */
2715 /* Don't need to do anything */
2717 case 1: /* LISTING_FOLLOWS */
2718 if (to_receive && !*to_receive && bytes_to_receive) {
2719 *to_receive = CtdlIPCReadListing(ipc, NULL);
2720 } else { /* Drain */
2721 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2725 case 4: /* SEND_LISTING */
2727 CtdlIPCSendListing(ipc, to_send);
2729 /* No listing given, fake it */
2730 CtdlIPC_putline(ipc, "000");
2734 case 6: /* BINARY_FOLLOWS */
2735 if (to_receive && !*to_receive && bytes_to_receive) {
2736 *bytes_to_receive = extract_long(proto_response, 0);
2737 *to_receive = (char *)
2738 malloc((size_t) * bytes_to_receive);
2742 serv_read(ipc, *to_receive, *bytes_to_receive);
2748 drain = extract_long(proto_response, 0);
2749 while (drain > SIZ) {
2750 serv_read(ipc, buf, SIZ);
2753 serv_read(ipc, buf, drain);
2757 case 7: /* SEND_BINARY */
2758 if (to_send && bytes_to_send) {
2759 serv_write(ipc, to_send, bytes_to_send);
2760 } else if (bytes_to_send) {
2761 /* Fake it, send nulls */
2764 fake = bytes_to_send;
2765 memset(buf, '\0', SIZ);
2766 while (fake > SIZ) {
2767 serv_write(ipc, buf, SIZ);
2770 serv_write(ipc, buf, fake);
2772 } /* else who knows? DANGER WILL ROBINSON */
2774 case 8: /* START_CHAT_MODE */
2775 if (!strncasecmp(command, "CHAT", 4)) {
2776 /* Don't call chatmode with generic! */
2777 CtdlIPC_putline(ipc, "/quit");
2780 /* In this mode we send then receive listing */
2782 CtdlIPCSendListing(ipc, to_send);
2784 /* No listing given, fake it */
2785 CtdlIPC_putline(ipc, "000");
2788 if (to_receive && !*to_receive && bytes_to_receive) {
2789 *to_receive = CtdlIPCReadListing(ipc, NULL);
2790 } else { /* Drain */
2791 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2796 case 9: /* ASYNC_MSG */
2797 /* CtdlIPCDoAsync(ret, proto_response); */
2798 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2804 CtdlIPC_unlock(ipc);
2810 * Connect to a Citadel on a remote host using a TCP/IP socket
2812 static int tcp_connectsock(char *host, char *service)
2814 struct in6_addr serveraddr;
2815 struct addrinfo hints;
2816 struct addrinfo *res = NULL;
2817 struct addrinfo *ai = NULL;
2821 if ((host == NULL) || IsEmptyStr(host)) {
2822 service = DEFAULT_HOST;
2824 if ((service == NULL) || IsEmptyStr(service)) {
2825 service = DEFAULT_PORT;
2828 memset(&hints, 0x00, sizeof(hints));
2829 hints.ai_flags = AI_NUMERICSERV;
2830 hints.ai_family = AF_UNSPEC;
2831 hints.ai_socktype = SOCK_STREAM;
2834 * Handle numeric IPv4 and IPv6 addresses
2836 rc = inet_pton(AF_INET, host, &serveraddr);
2837 if (rc == 1) { /* dotted quad */
2838 hints.ai_family = AF_INET;
2839 hints.ai_flags |= AI_NUMERICHOST;
2841 rc = inet_pton(AF_INET6, host, &serveraddr);
2842 if (rc == 1) { /* IPv6 address */
2843 hints.ai_family = AF_INET6;
2844 hints.ai_flags |= AI_NUMERICHOST;
2848 /* Begin the connection process */
2850 rc = getaddrinfo(host, service, &hints, &res);
2856 * Try all available addresses until we connect to one or until we run out.
2858 for (ai = res; ai != NULL; ai = ai->ai_next) {
2859 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2863 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2865 return (sock); /* Connected! */
2867 close(sock); /* Failed. Close the socket to avoid fd leak! */
2879 * Connect to a Citadel on the local host using a unix domain socket
2881 static int uds_connectsock(int *isLocal, char *sockpath)
2883 struct sockaddr_un addr;
2886 memset(&addr, 0, sizeof(addr));
2887 addr.sun_family = AF_UNIX;
2888 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2890 s = socket(AF_UNIX, SOCK_STREAM, 0);
2895 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2906 * input binary data from socket
2908 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes)
2910 unsigned int len, rlen;
2912 #if defined(HAVE_OPENSSL)
2914 serv_read_ssl(ipc, buf, bytes);
2919 while (len < bytes) {
2920 rlen = read(ipc->sock, &buf[len], bytes - len);
2922 connection_died(ipc, 0);
2931 * send binary to server
2933 void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
2935 unsigned int bytes_written = 0;
2938 #if defined(HAVE_OPENSSL)
2940 serv_write_ssl(ipc, buf, nbytes);
2944 while (bytes_written < nbytes) {
2945 retval = write(ipc->sock, &buf[bytes_written], nbytes - bytes_written);
2947 connection_died(ipc, 0);
2950 bytes_written += retval;
2957 * input binary data from encrypted connection
2959 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes)
2965 while (len < bytes) {
2966 if (SSL_want_read(ipc->ssl)) {
2967 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2968 error_printf("SSL_write in serv_read:\n");
2969 ERR_print_errors_fp(stderr);
2972 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2976 errval = SSL_get_error(ipc->ssl, rlen);
2977 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
2982 Not sure why we'd want to handle these error codes any differently,
2983 but this definitely isn't the way to handle them. Someone must have
2984 naively assumed that we could fall back to unencrypted communications,
2985 but all it does is just recursively blow the stack.
2986 if (errval == SSL_ERROR_ZERO_RETURN ||
2987 errval == SSL_ERROR_SSL) {
2988 serv_read(ipc, &buf[len], bytes - len);
2992 error_printf("SSL_read in serv_read: %s\n", ERR_reason_error_string(ERR_peek_error()));
2993 connection_died(ipc, 1);
3002 * send binary to server encrypted
3004 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
3006 unsigned int bytes_written = 0;
3010 while (bytes_written < nbytes) {
3011 if (SSL_want_write(ipc->ssl)) {
3012 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
3013 error_printf("SSL_read in serv_write:\n");
3014 ERR_print_errors_fp(stderr);
3017 retval = SSL_write(ipc->ssl, &buf[bytes_written], nbytes - bytes_written);
3021 errval = SSL_get_error(ipc->ssl, retval);
3022 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
3026 if (errval == SSL_ERROR_ZERO_RETURN || errval == SSL_ERROR_SSL) {
3027 serv_write(ipc, &buf[bytes_written], nbytes - bytes_written);
3030 error_printf("SSL_write in serv_write: %s\n", ERR_reason_error_string(ERR_peek_error()));
3031 connection_died(ipc, 1);
3034 bytes_written += retval;
3041 static void CtdlIPC_init_OpenSSL(void)
3044 const SSL_METHOD *ssl_method;
3047 /* already done init */
3056 SSL_load_error_strings();
3057 SSLeay_add_ssl_algorithms();
3059 /* Set up the SSL context in which we will oeprate */
3060 ssl_method = SSLv23_client_method();
3061 ssl_ctx = SSL_CTX_new(ssl_method);
3063 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
3066 /* Any reasonable cipher we can get */
3067 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
3068 error_printf("No ciphers available for encryption\n");
3071 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
3073 /* Load DH parameters into the context */
3076 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
3080 if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
3081 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
3086 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
3090 #endif /* HAVE_OPENSSL */
3093 int ReadNetworkChunk(CtdlIPC * ipc)
3108 FD_SET(ipc->sock, &read_fd);
3109 ret = select(ipc->sock + 1, &read_fd, NULL, NULL, &tv);
3113 *(ipc->BufPtr) = '\0';
3114 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3116 ipc->BufPtr[n] = '\0';
3121 } else if (ret < 0) {
3122 if (!(errno == EINTR || errno == EAGAIN))
3123 error_printf("\nselect failed: %d %s\n", err, strerror(err));
3129 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3131 ipc->BufPtr[n]='\0';
3136 connection_died(ipc, 0);
3144 * input string from socket - implemented in terms of serv_read()
3148 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3151 char *aptr, *bptr, *aeptr, *beptr;
3153 // error_printf("---\n");
3156 #if defined(HAVE_OPENSSL)
3159 /* Read one character at a time. */
3161 serv_read(ipc, &buf[i], 1);
3162 if (buf[i] == '\n' || i == (SIZ - 1))
3166 /* If we got a long line, discard characters until the newline. */
3168 while (buf[i] != '\n')
3169 serv_read(ipc, &buf[i], 1);
3171 /* Strip the trailing newline (and carriage return, if present) */
3172 if (i >= 0 && buf[i] == 10)
3174 if (i >= 0 && buf[i] == 13)
3179 if (ipc->Buf == NULL) {
3181 ipc->Buf = (char *) malloc(ipc->BufSize + 10);
3183 ipc->BufPtr = ipc->Buf;
3187 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3188 if (ipc->BufUsed == 0)
3189 ReadNetworkChunk(ipc);
3191 //// if (ipc->BufUsed != 0) while (1)
3196 aeptr = ipc->Buf + ipc->BufSize;
3197 while ((aptr < aeptr) && (bptr < beptr) && (*aptr != '\0') && (*aptr != '\n'))
3198 *(bptr++) = *(aptr++);
3199 if ((*aptr == '\n') && (aptr < aeptr)) {
3200 /* Terminate it right, remove the line breaks */
3201 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3203 while ((aptr < aeptr) && (*(aptr + 1) == '\0'))
3206 // 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);
3207 if ((bptr > buf + 1) && (*(bptr - 1) == '\r'))
3210 /* is there more in the buffer we need to read later? */
3211 if (ipc->Buf + ipc->BufUsed > aptr) {
3215 ipc->BufPtr = ipc->Buf;
3217 // error_printf("----bla6\n");
3220 } /* should we move our read stuf to the bufferstart so we have more space at the end? */
3221 else if ((ipc->BufPtr != ipc->Buf) && (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4)))) {
3222 size_t NewBufSize = ipc->BufSize * 2;
3223 int delta = (ipc->BufPtr - ipc->Buf);
3226 /* if the line would end after our buffer, we should use a bigger buffer. */
3227 NewBuf = (char *) malloc(NewBufSize + 10);
3228 memcpy(NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3230 ipc->Buf = ipc->BufPtr = NewBuf;
3231 ipc->BufUsed -= delta;
3232 ipc->BufSize = NewBufSize;
3234 if (ReadNetworkChunk(ipc) < 0) {
3235 // error_printf("----bla\n");
3239 /// error_printf("----bl45761%s\nipc->BufUsed");
3241 // error_printf("----bla1\n");
3244 #else /* CHUNKED_READ */
3246 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3250 /* Read one character at a time. */
3252 serv_read(ipc, &buf[i], 1);
3253 if (buf[i] == '\n' || i == (SIZ - 1))
3257 /* If we got a long line, discard characters until the newline. */
3259 while (buf[i] != '\n')
3260 serv_read(ipc, &buf[i], 1);
3262 /* Strip the trailing newline (and carriage return, if present) */
3263 if (i >= 0 && buf[i] == 10)
3265 if (i >= 0 && buf[i] == 13)
3270 #endif /* CHUNKED_READ */
3273 void CtdlIPC_chat_recv(CtdlIPC * ipc, char *buf)
3275 CtdlIPC_getline(ipc, buf);
3279 * send line to server - implemented in terms of serv_write()
3281 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf)
3287 cmd = malloc(len + 2);
3289 /* This requires no extra memory */
3290 serv_write(ipc, buf, len);
3291 serv_write(ipc, "\n", 1);
3293 /* This is network-optimized */
3294 strncpy(cmd, buf, len);
3295 strcpy(cmd + len, "\n");
3296 serv_write(ipc, cmd, len + 1);
3300 ipc->last_command_sent = time(NULL);
3303 void CtdlIPC_chat_send(CtdlIPC * ipc, const char *buf)
3305 CtdlIPC_putline(ipc, buf);
3312 CtdlIPC *CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3320 ipc = malloc(sizeof(struct _CtdlIPC));
3324 #if defined(HAVE_OPENSSL)
3326 CtdlIPC_init_OpenSSL();
3328 ipc->sock = -1; /* Not connected */
3329 ipc->isLocal = 0; /* Not local, of course! */
3330 ipc->downloading = 0;
3332 ipc->last_command_sent = 0L;
3333 ipc->network_status_cb = NULL;
3338 strcpy(cithost, DEFAULT_HOST); /* default host */
3339 strcpy(citport, DEFAULT_PORT); /* default port */
3341 /* Allow caller to supply our values */
3342 if (hostbuf && strlen(hostbuf) > 0) {
3343 strcpy(cithost, hostbuf);
3345 if (portbuf && strlen(portbuf) > 0) {
3346 strcpy(citport, portbuf);
3349 /* Read host/port from command line if present */
3350 for (a = 0; a < argc; ++a) {
3353 } else if (a == 1) {
3354 strcpy(cithost, argv[a]);
3355 } else if (a == 2) {
3356 strcpy(citport, argv[a]);
3358 error_printf("%s: usage: ", argv[0]);
3359 error_printf("%s [host] [port] ", argv[0]);
3366 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3370 /* If we're using a unix domain socket we can do a bunch of stuff */
3371 if (!strcmp(cithost, UDS)) {
3372 if (!strcasecmp(citport, DEFAULT_PORT)) {
3373 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3375 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3377 printf("[%s]\n", sockpath);
3378 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3379 if (ipc->sock == -1) {
3383 if (hostbuf != NULL)
3384 strcpy(hostbuf, cithost);
3385 if (portbuf != NULL)
3386 strcpy(portbuf, sockpath);
3387 strcpy(ipc->ip_hostname, "");
3388 strcpy(ipc->ip_address, "");
3392 printf("[%s:%s]\n", cithost, citport);
3393 ipc->sock = tcp_connectsock(cithost, citport);
3394 if (ipc->sock == -1) {
3400 /* Learn the actual network identity of the host to which we are connected */
3402 struct sockaddr_in6 clientaddr;
3403 unsigned int addrlen = sizeof(clientaddr);
3405 ipc->ip_hostname[0] = 0;
3406 ipc->ip_address[0] = 0;
3408 getpeername(ipc->sock, (struct sockaddr *) &clientaddr, &addrlen);
3409 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0);
3410 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST);
3412 /* stuff other things elsewhere */
3414 if (hostbuf != NULL)
3415 strcpy(hostbuf, cithost);
3416 if (portbuf != NULL)
3417 strcpy(portbuf, citport);
3423 * Disconnect and delete the IPC class (destructor)
3425 void CtdlIPC_delete(CtdlIPC * ipc)
3429 SSL_shutdown(ipc->ssl);
3434 if (ipc->sock > -1) {
3435 shutdown(ipc->sock, 2); /* Close it up */
3438 if (ipc->Buf != NULL)
3447 * Disconnect and delete the IPC class (destructor)
3448 * Also NULLs out the pointer
3450 void CtdlIPC_delete_ptr(CtdlIPC ** pipc)
3452 CtdlIPC_delete(*pipc);
3458 * return the file descriptor of the server socket so we can select() on it.
3460 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3463 int CtdlIPC_getsockfd(CtdlIPC * ipc)
3470 * return one character
3472 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3475 char CtdlIPC_get(CtdlIPC * ipc)
3480 serv_read(ipc, buf, 1);