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 int CtdlIPCChangeUsername(CtdlIPC * ipc, const char *username, char *cret)
1971 aaa = (char *) malloc(strlen(username) + 6);
1975 sprintf(aaa, "UCHG %s", username);
1976 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1983 /* This function returns the actual server time reported, or 0 if error */
1984 time_t CtdlIPCServerTime(CtdlIPC * ipc, char *cret)
1989 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1990 if (ret / 100 == 2) {
1991 tret = extract_long(cret, 0);
2000 int CtdlIPCAideGetUserParameters(CtdlIPC * ipc, const char *who, struct ctdluser **uret, char *cret)
2010 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
2014 sprintf(aaa, "AGUP %s", who);
2015 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2017 if (ret / 100 == 2) {
2018 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
2019 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
2020 uret[0]->flags = extract_int(cret, 2);
2021 uret[0]->timescalled = extract_long(cret, 3);
2022 uret[0]->posted = extract_long(cret, 4);
2023 uret[0]->axlevel = extract_int(cret, 5);
2024 uret[0]->usernum = extract_long(cret, 6);
2025 uret[0]->lastcall = extract_long(cret, 7);
2026 uret[0]->USuserpurge = extract_int(cret, 8);
2033 int CtdlIPCAideSetUserParameters(CtdlIPC * ipc, const struct ctdluser *uret, char *cret)
2043 aaa = (char *) malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
2047 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
2048 uret->fullname, uret->password, uret->flags, uret->timescalled,
2049 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge);
2050 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2057 int CtdlIPCAideGetEmailAddresses(CtdlIPC * ipc, const char *who, char *target_buf, char *cret)
2061 char *emailaddrs = NULL;
2062 size_t emailaddrs_len = 0;
2064 sprintf(aaa, "AGEA %s", who);
2065 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
2067 if (ret / 100 == 1) {
2068 strcpy(target_buf, emailaddrs);
2071 if (emailaddrs != NULL) {
2080 int CtdlIPCAideSetEmailAddresses(CtdlIPC * ipc, const char *who, char *emailaddrs, char *cret)
2092 sprintf(aaa, "ASEA %s", who);
2093 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2099 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2100 /* caller must free the struct ExpirePolicy */
2101 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC * ipc, GPEXWhichPolicy which, struct ExpirePolicy **policy, char *cret)
2103 static char *proto[] = {
2107 strof(mailboxespolicy)
2117 *policy = (struct ExpirePolicy *) calloc(1, sizeof(struct ExpirePolicy));
2120 if (which < 0 || which > 3)
2123 sprintf(cmd, "GPEX %s", proto[which]);
2124 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2125 if (ret / 100 == 2) {
2126 policy[0]->expire_mode = extract_int(cret, 0);
2127 policy[0]->expire_value = extract_int(cret, 1);
2134 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2135 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2136 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC * ipc, int which, struct ExpirePolicy *policy, char *cret)
2139 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2143 if (which < 0 || which > 3)
2147 if (policy->expire_mode < 0 || policy->expire_mode > 3)
2149 if (policy->expire_mode >= 2 && policy->expire_value < 1)
2152 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which], policy->expire_mode, policy->expire_value);
2153 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2158 int CtdlIPCGetSystemConfig(CtdlIPC * ipc, char **listing, char *cret)
2169 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0, listing, &bytes, cret);
2174 int CtdlIPCSetSystemConfig(CtdlIPC * ipc, const char *listing, char *cret)
2181 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing), NULL, NULL, cret);
2186 int CtdlIPCGetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, char **listing, char *cret)
2201 aaa = malloc(strlen(mimetype) + 13);
2204 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2205 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2212 int CtdlIPCSetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, const char *listing, char *cret)
2224 aaa = malloc(strlen(mimetype) + 13);
2227 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2228 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2235 int CtdlIPCGetRoomNetworkConfig(CtdlIPC * ipc, char **listing, char *cret)
2246 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2251 int CtdlIPCSetRoomNetworkConfig(CtdlIPC * ipc, const char *listing, char *cret)
2258 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2263 int CtdlIPCRequestClientLogout(CtdlIPC * ipc, int session, char *cret)
2272 sprintf(aaa, "REQT %d", session);
2273 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2278 int CtdlIPCSetMessageSeen(CtdlIPC * ipc, long msgnum, int seen, char *cret)
2287 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2288 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2293 int CtdlIPCStartEncryption(CtdlIPC * ipc, char *cret)
2302 /* New SSL object */
2303 temp_ssl = SSL_new(ssl_ctx);
2305 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2308 /* Pointless flag waving */
2309 #if SSLEAY_VERSION_NUMBER >= 0x0922
2310 SSL_set_session_id_context(temp_ssl, (const unsigned char *) "Citadel SID", 14);
2313 /* Associate network connection with SSL object */
2314 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2315 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2319 if (status_hook != NULL) {
2320 status_hook("Requesting encryption...\r");
2323 /* Ready to start SSL/TLS */
2324 r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2326 error_printf("Server can't start TLS: %s\n", buf);
2331 /* Do SSL/TLS handshake */
2332 if ((a = SSL_connect(temp_ssl)) < 1) {
2333 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2337 ipc->ssl = temp_ssl;
2339 error_printf("Encrypting with %s cipher %s\n",
2340 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)), SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2345 #endif /* HAVE_OPENSSL */
2350 static void endtls(SSL * ssl)
2361 int CtdlIPCDirectoryLookup(CtdlIPC * ipc, const char *address, char *cret)
2371 aaa = (char *) malloc(strlen(address) + 6);
2375 sprintf(aaa, "QDIR %s", address);
2376 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2383 int CtdlIPCInternalProgram(CtdlIPC * ipc, int secret, char *cret)
2389 sprintf(aaa, "IPGM %d", secret);
2390 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2396 /* ************************************************************************** */
2397 /* Stuff below this line is not for public consumption */
2398 /* ************************************************************************** */
2401 /* Read a listing from the server up to 000. Append to dest if it exists */
2402 char *CtdlIPCReadListing(CtdlIPC * ipc, char *dest)
2411 length = strlen(ret);
2416 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2417 linelength = strlen(aaa);
2418 ret = (char *) realloc(ret, (size_t) (length + linelength + 2));
2420 strcpy(&ret[length], aaa);
2421 length += linelength;
2422 strcpy(&ret[length++], "\n");
2430 /* Send a listing to the server; generate the ending 000. */
2431 int CtdlIPCSendListing(CtdlIPC * ipc, const char *listing)
2435 text = (char *) malloc(strlen(listing) + 6);
2437 strcpy(text, listing);
2438 while (text[strlen(text) - 1] == '\n')
2439 text[strlen(text) - 1] = '\0';
2440 strcat(text, "\n000");
2441 CtdlIPC_putline(ipc, text);
2445 /* Malloc failed but we are committed to send */
2446 /* This may result in extra blanks at the bottom */
2447 CtdlIPC_putline(ipc, text);
2448 CtdlIPC_putline(ipc, "000");
2454 /* Partial read of file from server */
2455 size_t CtdlIPCPartialRead(CtdlIPC * ipc, void **buf, size_t offset, size_t bytes, char *cret)
2468 sprintf(aaa, "READ %d|%d", (int) offset, (int) bytes);
2469 CtdlIPC_putline(ipc, aaa);
2470 CtdlIPC_getline(ipc, aaa);
2472 strcpy(cret, &aaa[4]);
2474 len = extract_long(&aaa[4], 0);
2475 *buf = (void *) realloc(*buf, (size_t) (offset + len));
2477 /* I know what I'm doing */
2478 serv_read(ipc, ((char *) (*buf) + offset), len);
2480 /* We have to read regardless */
2481 serv_read(ipc, aaa, len);
2485 CtdlIPC_unlock(ipc);
2491 int CtdlIPCEndDownload(CtdlIPC * ipc, char *cret)
2497 if (!ipc->downloading)
2500 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2502 ipc->downloading = 0;
2508 int CtdlIPCSpecifyPreferredFormats(CtdlIPC * ipc, char *cret, char *formats)
2513 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2514 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2521 int CtdlIPCReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2522 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2532 if (!ipc->downloading)
2536 if (progress_gauge_callback)
2537 progress_gauge_callback(ipc, len, bytes);
2538 while (len < bytes) {
2541 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2547 if (progress_gauge_callback)
2548 progress_gauge_callback(ipc, len, bytes);
2553 /* READ - pipelined */
2554 int CtdlIPCHighSpeedReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2555 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2558 int calls; /* How many calls in the pipeline */
2559 int i; /* iterator */
2568 if (!ipc->downloading)
2571 *buf = (void *) realloc(*buf, bytes - resume);
2577 if (progress_gauge_callback)
2578 progress_gauge_callback(ipc, len, bytes);
2580 /* How many calls will be in the pipeline? */
2581 calls = (bytes - resume) / 4096;
2582 if ((bytes - resume) % 4096)
2585 /* Send all requests at once */
2586 for (i = 0; i < calls; i++) {
2587 sprintf(aaa, "READ %d|4096", (int) (i * 4096 + resume));
2588 CtdlIPC_putline(ipc, aaa);
2591 /* Receive all responses at once */
2592 for (i = 0; i < calls; i++) {
2593 CtdlIPC_getline(ipc, aaa);
2595 strcpy(cret, &aaa[4]);
2597 len = extract_long(&aaa[4], 0);
2598 /* I know what I'm doing */
2599 serv_read(ipc, ((char *) (*buf) + (i * 4096)), len);
2601 if (progress_gauge_callback)
2602 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2604 CtdlIPC_unlock(ipc);
2610 int CtdlIPCEndUpload(CtdlIPC * ipc, int discard, char *cret)
2617 if (!ipc->uploading)
2620 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2621 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2628 int CtdlIPCWriteUpload(CtdlIPC * ipc, FILE * uploadFP, void (*progress_gauge_callback)
2629 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2636 FILE *fd = uploadFP;
2642 fseek(fd, 0L, SEEK_END);
2646 if (progress_gauge_callback)
2647 progress_gauge_callback(ipc, 0, bytes);
2649 while (offset < bytes) {
2652 /* Read some data in */
2653 to_write = fread(buf, 1, 4096, fd);
2655 if (feof(fd) || ferror(fd))
2658 sprintf(aaa, "WRIT %d", (int) to_write);
2659 CtdlIPC_putline(ipc, aaa);
2660 CtdlIPC_getline(ipc, aaa);
2661 strcpy(cret, &aaa[4]);
2663 if (aaa[0] == '7') {
2664 to_write = extract_long(&aaa[4], 0);
2666 serv_write(ipc, buf, to_write);
2668 if (progress_gauge_callback)
2669 progress_gauge_callback(ipc, offset, bytes);
2670 /* Detect short reads and back up if needed */
2671 /* offset will never be negative anyway */
2672 fseek(fd, (signed) offset, SEEK_SET);
2677 if (progress_gauge_callback)
2678 progress_gauge_callback(ipc, 1, 1);
2681 return (!ferr ? ret : -2);
2686 * Generic command method. This method should handle any server command
2687 * except for CHAT. It takes the following arguments:
2689 * ipc The server to speak with
2690 * command Preformatted command to send to server
2691 * to_send A text or binary file to send to server
2692 * (only sent if server requests it)
2693 * bytes_to_send The number of bytes in to_send (required if
2694 * sending binary, optional if sending listing)
2695 * to_receive Pointer to a NULL pointer, if the server
2696 * sends text or binary we will allocate memory
2697 * for the file and stuff it here
2698 * bytes_to_receive If a file is received, we will store its
2700 * proto_response The protocol response. Caller must provide
2701 * this buffer and ensure that it is at least
2702 * 128 bytes in length.
2704 * This function returns a number equal to the protocol response number,
2705 * -1 if an internal error occurred, -2 if caller provided bad values,
2706 * or 0 - the protocol response number if bad values were found during
2707 * the protocol exchange.
2708 * It stores the protocol response string (minus the number) in
2709 * protocol_response as described above. Some commands send additional
2710 * data in this string.
2712 int CtdlIPCGenericCommand(CtdlIPC * ipc,
2713 const char *command, const char *to_send,
2714 size_t bytes_to_send, char **to_receive, size_t * bytes_to_receive, char *proto_response)
2721 if (!proto_response)
2725 CtdlIPC_putline(ipc, command);
2727 CtdlIPC_getline(ipc, proto_response);
2728 if (proto_response[3] == '*')
2730 ret = atoi(proto_response);
2731 strcpy(proto_response, &proto_response[4]);
2732 switch (ret / 100) {
2733 default: /* Unknown, punt */
2735 case 3: /* MORE_DATA */
2737 /* Don't need to do anything */
2739 case 1: /* LISTING_FOLLOWS */
2740 if (to_receive && !*to_receive && bytes_to_receive) {
2741 *to_receive = CtdlIPCReadListing(ipc, NULL);
2742 } else { /* Drain */
2743 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2747 case 4: /* SEND_LISTING */
2749 CtdlIPCSendListing(ipc, to_send);
2751 /* No listing given, fake it */
2752 CtdlIPC_putline(ipc, "000");
2756 case 6: /* BINARY_FOLLOWS */
2757 if (to_receive && !*to_receive && bytes_to_receive) {
2758 *bytes_to_receive = extract_long(proto_response, 0);
2759 *to_receive = (char *)
2760 malloc((size_t) * bytes_to_receive);
2764 serv_read(ipc, *to_receive, *bytes_to_receive);
2770 drain = extract_long(proto_response, 0);
2771 while (drain > SIZ) {
2772 serv_read(ipc, buf, SIZ);
2775 serv_read(ipc, buf, drain);
2779 case 7: /* SEND_BINARY */
2780 if (to_send && bytes_to_send) {
2781 serv_write(ipc, to_send, bytes_to_send);
2782 } else if (bytes_to_send) {
2783 /* Fake it, send nulls */
2786 fake = bytes_to_send;
2787 memset(buf, '\0', SIZ);
2788 while (fake > SIZ) {
2789 serv_write(ipc, buf, SIZ);
2792 serv_write(ipc, buf, fake);
2794 } /* else who knows? DANGER WILL ROBINSON */
2796 case 8: /* START_CHAT_MODE */
2797 if (!strncasecmp(command, "CHAT", 4)) {
2798 /* Don't call chatmode with generic! */
2799 CtdlIPC_putline(ipc, "/quit");
2802 /* In this mode we send then receive listing */
2804 CtdlIPCSendListing(ipc, to_send);
2806 /* No listing given, fake it */
2807 CtdlIPC_putline(ipc, "000");
2810 if (to_receive && !*to_receive && bytes_to_receive) {
2811 *to_receive = CtdlIPCReadListing(ipc, NULL);
2812 } else { /* Drain */
2813 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2818 case 9: /* ASYNC_MSG */
2819 /* CtdlIPCDoAsync(ret, proto_response); */
2820 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2826 CtdlIPC_unlock(ipc);
2832 * Connect to a Citadel on a remote host using a TCP/IP socket
2834 static int tcp_connectsock(char *host, char *service)
2836 struct in6_addr serveraddr;
2837 struct addrinfo hints;
2838 struct addrinfo *res = NULL;
2839 struct addrinfo *ai = NULL;
2843 if ((host == NULL) || IsEmptyStr(host)) {
2844 service = DEFAULT_HOST;
2846 if ((service == NULL) || IsEmptyStr(service)) {
2847 service = DEFAULT_PORT;
2850 memset(&hints, 0x00, sizeof(hints));
2851 hints.ai_flags = AI_NUMERICSERV;
2852 hints.ai_family = AF_UNSPEC;
2853 hints.ai_socktype = SOCK_STREAM;
2856 * Handle numeric IPv4 and IPv6 addresses
2858 rc = inet_pton(AF_INET, host, &serveraddr);
2859 if (rc == 1) { /* dotted quad */
2860 hints.ai_family = AF_INET;
2861 hints.ai_flags |= AI_NUMERICHOST;
2863 rc = inet_pton(AF_INET6, host, &serveraddr);
2864 if (rc == 1) { /* IPv6 address */
2865 hints.ai_family = AF_INET6;
2866 hints.ai_flags |= AI_NUMERICHOST;
2870 /* Begin the connection process */
2872 rc = getaddrinfo(host, service, &hints, &res);
2878 * Try all available addresses until we connect to one or until we run out.
2880 for (ai = res; ai != NULL; ai = ai->ai_next) {
2881 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2885 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2887 return (sock); /* Connected! */
2889 close(sock); /* Failed. Close the socket to avoid fd leak! */
2901 * Connect to a Citadel on the local host using a unix domain socket
2903 static int uds_connectsock(int *isLocal, char *sockpath)
2905 struct sockaddr_un addr;
2908 memset(&addr, 0, sizeof(addr));
2909 addr.sun_family = AF_UNIX;
2910 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2912 s = socket(AF_UNIX, SOCK_STREAM, 0);
2917 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2928 * input binary data from socket
2930 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes)
2932 unsigned int len, rlen;
2934 #if defined(HAVE_OPENSSL)
2936 serv_read_ssl(ipc, buf, bytes);
2941 while (len < bytes) {
2942 rlen = read(ipc->sock, &buf[len], bytes - len);
2944 connection_died(ipc, 0);
2953 * send binary to server
2955 void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
2957 unsigned int bytes_written = 0;
2960 #if defined(HAVE_OPENSSL)
2962 serv_write_ssl(ipc, buf, nbytes);
2966 while (bytes_written < nbytes) {
2967 retval = write(ipc->sock, &buf[bytes_written], nbytes - bytes_written);
2969 connection_died(ipc, 0);
2972 bytes_written += retval;
2979 * input binary data from encrypted connection
2981 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes)
2987 while (len < bytes) {
2988 if (SSL_want_read(ipc->ssl)) {
2989 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2990 error_printf("SSL_write in serv_read:\n");
2991 ERR_print_errors_fp(stderr);
2994 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2998 errval = SSL_get_error(ipc->ssl, rlen);
2999 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
3004 Not sure why we'd want to handle these error codes any differently,
3005 but this definitely isn't the way to handle them. Someone must have
3006 naively assumed that we could fall back to unencrypted communications,
3007 but all it does is just recursively blow the stack.
3008 if (errval == SSL_ERROR_ZERO_RETURN ||
3009 errval == SSL_ERROR_SSL) {
3010 serv_read(ipc, &buf[len], bytes - len);
3014 error_printf("SSL_read in serv_read: %s\n", ERR_reason_error_string(ERR_peek_error()));
3015 connection_died(ipc, 1);
3024 * send binary to server encrypted
3026 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
3028 unsigned int bytes_written = 0;
3032 while (bytes_written < nbytes) {
3033 if (SSL_want_write(ipc->ssl)) {
3034 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
3035 error_printf("SSL_read in serv_write:\n");
3036 ERR_print_errors_fp(stderr);
3039 retval = SSL_write(ipc->ssl, &buf[bytes_written], nbytes - bytes_written);
3043 errval = SSL_get_error(ipc->ssl, retval);
3044 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
3048 if (errval == SSL_ERROR_ZERO_RETURN || errval == SSL_ERROR_SSL) {
3049 serv_write(ipc, &buf[bytes_written], nbytes - bytes_written);
3052 error_printf("SSL_write in serv_write: %s\n", ERR_reason_error_string(ERR_peek_error()));
3053 connection_died(ipc, 1);
3056 bytes_written += retval;
3063 static void CtdlIPC_init_OpenSSL(void)
3066 const SSL_METHOD *ssl_method;
3069 /* already done init */
3078 SSL_load_error_strings();
3079 SSLeay_add_ssl_algorithms();
3081 /* Set up the SSL context in which we will oeprate */
3082 ssl_method = SSLv23_client_method();
3083 ssl_ctx = SSL_CTX_new(ssl_method);
3085 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
3088 /* Any reasonable cipher we can get */
3089 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
3090 error_printf("No ciphers available for encryption\n");
3093 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
3095 /* Load DH parameters into the context */
3098 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
3102 if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
3103 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
3108 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
3112 #endif /* HAVE_OPENSSL */
3115 int ReadNetworkChunk(CtdlIPC * ipc)
3130 FD_SET(ipc->sock, &read_fd);
3131 ret = select(ipc->sock + 1, &read_fd, NULL, NULL, &tv);
3135 *(ipc->BufPtr) = '\0';
3136 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3138 ipc->BufPtr[n] = '\0';
3143 } else if (ret < 0) {
3144 if (!(errno == EINTR || errno == EAGAIN))
3145 error_printf("\nselect failed: %d %s\n", err, strerror(err));
3151 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3153 ipc->BufPtr[n]='\0';
3158 connection_died(ipc, 0);
3166 * input string from socket - implemented in terms of serv_read()
3170 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3173 char *aptr, *bptr, *aeptr, *beptr;
3175 // error_printf("---\n");
3178 #if defined(HAVE_OPENSSL)
3181 /* Read one character at a time. */
3183 serv_read(ipc, &buf[i], 1);
3184 if (buf[i] == '\n' || i == (SIZ - 1))
3188 /* If we got a long line, discard characters until the newline. */
3190 while (buf[i] != '\n')
3191 serv_read(ipc, &buf[i], 1);
3193 /* Strip the trailing newline (and carriage return, if present) */
3194 if (i >= 0 && buf[i] == 10)
3196 if (i >= 0 && buf[i] == 13)
3201 if (ipc->Buf == NULL) {
3203 ipc->Buf = (char *) malloc(ipc->BufSize + 10);
3205 ipc->BufPtr = ipc->Buf;
3209 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3210 if (ipc->BufUsed == 0)
3211 ReadNetworkChunk(ipc);
3213 //// if (ipc->BufUsed != 0) while (1)
3218 aeptr = ipc->Buf + ipc->BufSize;
3219 while ((aptr < aeptr) && (bptr < beptr) && (*aptr != '\0') && (*aptr != '\n'))
3220 *(bptr++) = *(aptr++);
3221 if ((*aptr == '\n') && (aptr < aeptr)) {
3222 /* Terminate it right, remove the line breaks */
3223 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3225 while ((aptr < aeptr) && (*(aptr + 1) == '\0'))
3228 // 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);
3229 if ((bptr > buf + 1) && (*(bptr - 1) == '\r'))
3232 /* is there more in the buffer we need to read later? */
3233 if (ipc->Buf + ipc->BufUsed > aptr) {
3237 ipc->BufPtr = ipc->Buf;
3239 // error_printf("----bla6\n");
3242 } /* should we move our read stuf to the bufferstart so we have more space at the end? */
3243 else if ((ipc->BufPtr != ipc->Buf) && (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4)))) {
3244 size_t NewBufSize = ipc->BufSize * 2;
3245 int delta = (ipc->BufPtr - ipc->Buf);
3248 /* if the line would end after our buffer, we should use a bigger buffer. */
3249 NewBuf = (char *) malloc(NewBufSize + 10);
3250 memcpy(NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3252 ipc->Buf = ipc->BufPtr = NewBuf;
3253 ipc->BufUsed -= delta;
3254 ipc->BufSize = NewBufSize;
3256 if (ReadNetworkChunk(ipc) < 0) {
3257 // error_printf("----bla\n");
3261 /// error_printf("----bl45761%s\nipc->BufUsed");
3263 // error_printf("----bla1\n");
3266 #else /* CHUNKED_READ */
3268 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3272 /* Read one character at a time. */
3274 serv_read(ipc, &buf[i], 1);
3275 if (buf[i] == '\n' || i == (SIZ - 1))
3279 /* If we got a long line, discard characters until the newline. */
3281 while (buf[i] != '\n')
3282 serv_read(ipc, &buf[i], 1);
3284 /* Strip the trailing newline (and carriage return, if present) */
3285 if (i >= 0 && buf[i] == 10)
3287 if (i >= 0 && buf[i] == 13)
3292 #endif /* CHUNKED_READ */
3295 void CtdlIPC_chat_recv(CtdlIPC * ipc, char *buf)
3297 CtdlIPC_getline(ipc, buf);
3301 * send line to server - implemented in terms of serv_write()
3303 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf)
3309 cmd = malloc(len + 2);
3311 /* This requires no extra memory */
3312 serv_write(ipc, buf, len);
3313 serv_write(ipc, "\n", 1);
3315 /* This is network-optimized */
3316 strncpy(cmd, buf, len);
3317 strcpy(cmd + len, "\n");
3318 serv_write(ipc, cmd, len + 1);
3322 ipc->last_command_sent = time(NULL);
3325 void CtdlIPC_chat_send(CtdlIPC * ipc, const char *buf)
3327 CtdlIPC_putline(ipc, buf);
3334 CtdlIPC *CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3342 ipc = malloc(sizeof(struct _CtdlIPC));
3346 #if defined(HAVE_OPENSSL)
3348 CtdlIPC_init_OpenSSL();
3350 ipc->sock = -1; /* Not connected */
3351 ipc->isLocal = 0; /* Not local, of course! */
3352 ipc->downloading = 0;
3354 ipc->last_command_sent = 0L;
3355 ipc->network_status_cb = NULL;
3360 strcpy(cithost, DEFAULT_HOST); /* default host */
3361 strcpy(citport, DEFAULT_PORT); /* default port */
3363 /* Allow caller to supply our values */
3364 if (hostbuf && strlen(hostbuf) > 0) {
3365 strcpy(cithost, hostbuf);
3367 if (portbuf && strlen(portbuf) > 0) {
3368 strcpy(citport, portbuf);
3371 /* Read host/port from command line if present */
3372 for (a = 0; a < argc; ++a) {
3375 } else if (a == 1) {
3376 strcpy(cithost, argv[a]);
3377 } else if (a == 2) {
3378 strcpy(citport, argv[a]);
3380 error_printf("%s: usage: ", argv[0]);
3381 error_printf("%s [host] [port] ", argv[0]);
3388 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3392 /* If we're using a unix domain socket we can do a bunch of stuff */
3393 if (!strcmp(cithost, UDS)) {
3394 if (!strcasecmp(citport, DEFAULT_PORT)) {
3395 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3397 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3399 printf("[%s]\n", sockpath);
3400 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3401 if (ipc->sock == -1) {
3405 if (hostbuf != NULL)
3406 strcpy(hostbuf, cithost);
3407 if (portbuf != NULL)
3408 strcpy(portbuf, sockpath);
3409 strcpy(ipc->ip_hostname, "");
3410 strcpy(ipc->ip_address, "");
3414 printf("[%s:%s]\n", cithost, citport);
3415 ipc->sock = tcp_connectsock(cithost, citport);
3416 if (ipc->sock == -1) {
3422 /* Learn the actual network identity of the host to which we are connected */
3424 struct sockaddr_in6 clientaddr;
3425 unsigned int addrlen = sizeof(clientaddr);
3427 ipc->ip_hostname[0] = 0;
3428 ipc->ip_address[0] = 0;
3430 getpeername(ipc->sock, (struct sockaddr *) &clientaddr, &addrlen);
3431 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0);
3432 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST);
3434 /* stuff other things elsewhere */
3436 if (hostbuf != NULL)
3437 strcpy(hostbuf, cithost);
3438 if (portbuf != NULL)
3439 strcpy(portbuf, citport);
3445 * Disconnect and delete the IPC class (destructor)
3447 void CtdlIPC_delete(CtdlIPC * ipc)
3451 SSL_shutdown(ipc->ssl);
3456 if (ipc->sock > -1) {
3457 shutdown(ipc->sock, 2); /* Close it up */
3460 if (ipc->Buf != NULL)
3469 * Disconnect and delete the IPC class (destructor)
3470 * Also NULLs out the pointer
3472 void CtdlIPC_delete_ptr(CtdlIPC ** pipc)
3474 CtdlIPC_delete(*pipc);
3480 * return the file descriptor of the server socket so we can select() on it.
3482 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3485 int CtdlIPC_getsockfd(CtdlIPC * ipc)
3492 * return one character
3494 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3497 char CtdlIPC_get(CtdlIPC * ipc)
3502 serv_read(ipc, buf, 1);