2 * Copyright (c) 1987-2020 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 mret[0]->is_local = 0;
626 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
627 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
628 if (ret / 100 == 1) {
630 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
631 while (strlen(bbb) > 4 && bbb[4] == '=') {
632 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
633 remove_token(bbb, 0, '\n');
635 if (!strncasecmp(aaa, "nhdr=yes", 8))
637 else if (!strncasecmp(aaa, "from=", 5))
638 safestrncpy(mret[0]->author, &aaa[5], SIZ);
639 else if (!strncasecmp(aaa, "type=", 5))
640 mret[0]->type = atoi(&aaa[5]);
641 else if (!strncasecmp(aaa, "msgn=", 5))
642 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
643 else if (!strncasecmp(aaa, "subj=", 5))
644 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
645 else if (!strncasecmp(aaa, "rfca=", 5))
646 safestrncpy(mret[0]->email, &aaa[5], SIZ);
647 else if (!strncasecmp(aaa, "room=", 5))
648 safestrncpy(mret[0]->room, &aaa[5], SIZ);
649 else if (!strncasecmp(aaa, "rcpt=", 5))
650 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
651 else if (!strncasecmp(aaa, "wefw=", 5))
652 safestrncpy(mret[0]->references, &aaa[5], SIZ);
653 else if (!strncasecmp(aaa, "time=", 5))
654 mret[0]->time = atol(&aaa[5]);
655 else if (!strncasecmp(aaa, "locl", 4))
656 mret[0]->is_local = 1;
658 /* Multipart/alternative prefix & suffix strings help
659 * us to determine which part we want to download.
661 else if (!strncasecmp(aaa, "pref=", 5)) {
662 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
663 if (!strcasecmp(multipart_prefix, "multipart/alternative")) {
666 } else if (!strncasecmp(aaa, "suff=", 5)) {
667 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
668 if (!strcasecmp(multipart_prefix, "multipart/alternative")) {
673 else if (!strncasecmp(aaa, "part=", 5)) {
674 struct parts *ptr, *chain;
676 ptr = (struct parts *) calloc(1, sizeof(struct parts));
679 /* Fill the buffers for the caller */
680 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
681 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
682 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
683 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
684 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
685 ptr->length = extract_long(&aaa[5], 5);
686 if (!mret[0]->attachments)
687 mret[0]->attachments = ptr;
689 chain = mret[0]->attachments;
695 /* Now handle multipart/alternative */
696 if (multipart_hunting > 0) {
697 if ((!strcasecmp(ptr->mimetype, "text/plain"))
698 || (!strcasecmp(ptr->mimetype, "text/html"))) {
699 strcpy(mret[0]->mime_chosen, ptr->number);
706 /* Eliminate "text\n" */
707 remove_token(bbb, 0, '\n');
709 /* If doing a MIME thing, pull out the extra headers */
712 if (!strncasecmp(bbb, "Content-type:", 13)) {
713 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
714 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
715 striplt(mret[0]->content_type);
717 /* strip out ";charset=" portion. FIXME do something with
718 * the charset (like... convert it) instead of just throwing
721 if (strstr(mret[0]->content_type, ";") != NULL) {
722 strcpy(strstr(mret[0]->content_type, ";"), "");
726 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
727 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
728 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
729 striplt(mret[0]->mime_chosen);
731 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
732 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
733 strcpy(encoding, &encoding[26]);
736 remove_token(bbb, 0, '\n');
737 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
738 remove_token(bbb, 0, '\n');
745 if ((!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable"))) {
747 int bytes_decoded = 0;
748 ccc = malloc(strlen(bbb) + 32768);
749 if (!strcasecmp(encoding, "base64")) {
750 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
751 } else if (!strcasecmp(encoding, "quoted-printable")) {
752 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
754 ccc[bytes_decoded] = 0;
759 /* FIXME: Strip trailing whitespace */
760 bbb = (char *) realloc(bbb, (size_t) (strlen(bbb) + 1));
763 bbb = (char *) realloc(bbb, 1);
773 int CtdlIPCWhoKnowsRoom(CtdlIPC * ipc, char **listing, char *cret)
785 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
791 int CtdlIPCServerInfo(CtdlIPC * ipc, char *cret)
795 char *listing = NULL;
801 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
802 if (ret / 100 == 1) {
805 while (*listing && strlen(listing)) {
806 extract_token(buf, listing, 0, '\n', sizeof buf);
807 remove_token(listing, 0, '\n');
810 ipc->ServInfo.pid = atoi(buf);
813 strcpy(ipc->ServInfo.nodename, buf);
816 strcpy(ipc->ServInfo.humannode, buf);
819 strcpy(ipc->ServInfo.fqdn, buf);
822 strcpy(ipc->ServInfo.software, buf);
825 ipc->ServInfo.rev_level = atoi(buf);
828 strcpy(ipc->ServInfo.site_location, buf);
831 strcpy(ipc->ServInfo.sysadm, buf);
834 strcpy(ipc->ServInfo.moreprompt, buf);
837 ipc->ServInfo.ok_floors = atoi(buf);
840 ipc->ServInfo.paging_level = atoi(buf);
843 ipc->ServInfo.supports_qnop = atoi(buf);
846 ipc->ServInfo.supports_ldap = atoi(buf);
849 ipc->ServInfo.newuser_disabled = atoi(buf);
852 strcpy(ipc->ServInfo.default_cal_zone, buf);
855 ipc->ServInfo.load_avg = atof(buf);
858 ipc->ServInfo.worker_avg = atof(buf);
861 ipc->ServInfo.thread_count = atoi(buf);
864 ipc->ServInfo.has_sieve = atoi(buf);
867 ipc->ServInfo.fulltext_enabled = atoi(buf);
870 strcpy(ipc->ServInfo.svn_revision, buf);
873 ipc->ServInfo.guest_logins = atoi(buf);
886 int CtdlIPCReadDirectory(CtdlIPC * ipc, char **listing, char *cret)
898 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
904 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
906 int CtdlIPCSetLastRead(CtdlIPC * ipc, long msgnum, char *cret)
915 sprintf(aaa, "SLRP %ld", msgnum);
917 sprintf(aaa, "SLRP HIGHEST");
919 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
925 int CtdlIPCInviteUserToRoom(CtdlIPC * ipc, const char *username, char *cret)
935 aaa = (char *) malloc(strlen(username) + 6);
939 sprintf(aaa, "INVT %s", username);
940 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
947 int CtdlIPCKickoutUserFromRoom(CtdlIPC * ipc, const char *username, char *cret)
957 aaa = (char *) malloc(strlen(username) + 6);
959 sprintf(aaa, "KICK %s", username);
960 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
967 int CtdlIPCGetRoomAttributes(CtdlIPC * ipc, struct ctdlroom **qret, char *cret)
976 *qret = (struct ctdlroom *) calloc(1, sizeof(struct ctdlroom));
980 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
981 if (ret / 100 == 2) {
982 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
983 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
984 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
985 qret[0]->QRflags = extract_int(cret, 3);
986 qret[0]->QRfloor = extract_int(cret, 4);
987 qret[0]->QRorder = extract_int(cret, 5);
988 qret[0]->QRdefaultview = extract_int(cret, 6);
989 qret[0]->QRflags2 = extract_int(cret, 7);
996 /* set forget to kick all users out of room */
997 int CtdlIPCSetRoomAttributes(CtdlIPC * ipc, int forget, struct ctdlroom *qret, char *cret)
1007 aaa = (char *) malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) + strlen(qret->QRdirname) + 64);
1011 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
1012 qret->QRname, qret->QRpasswd, qret->QRdirname,
1013 qret->QRflags, forget, qret->QRfloor, qret->QRorder, qret->QRdefaultview, qret->QRflags2);
1014 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1021 int CtdlIPCGetRoomAide(CtdlIPC * ipc, char *cret)
1026 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
1031 int CtdlIPCSetRoomAide(CtdlIPC * ipc, const char *username, char *cret)
1041 aaa = (char *) malloc(strlen(username) + 6);
1045 sprintf(aaa, "SETA %s", username);
1046 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1053 int CtdlIPCPostMessage(CtdlIPC * ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
1064 if (mr->references) {
1065 for (ptr = mr->references; *ptr != 0; ++ptr) {
1071 snprintf(cmd, sizeof cmd,
1072 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
1073 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
1074 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL, NULL, cret);
1075 if ((flag == 0) && (subject_required != NULL)) {
1076 /* Is the server strongly recommending that the user enter a message subject? */
1077 if ((cret[3] != '\0') && (cret[4] != '\0')) {
1078 *subject_required = extract_int(&cret[4], 1);
1088 int CtdlIPCRoomInfo(CtdlIPC * ipc, char **iret, char *cret)
1099 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
1104 int CtdlIPCDeleteMessage(CtdlIPC * ipc, long msgnum, char *cret)
1113 sprintf(aaa, "DELE %ld", msgnum);
1114 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1119 int CtdlIPCMoveMessage(CtdlIPC * ipc, int copy, long msgnum, const char *destroom, char *cret)
1131 aaa = (char *) malloc(strlen(destroom) + 28);
1135 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
1136 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1143 int CtdlIPCDeleteRoom(CtdlIPC * ipc, int for_real, char *cret)
1150 sprintf(aaa, "KILL %d", for_real);
1151 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1156 int CtdlIPCCreateRoom(CtdlIPC * ipc, int for_real, const char *roomname, int type, const char *password, int floor, char *cret)
1167 aaa = (char *) malloc(strlen(roomname) + strlen(password) + 40);
1170 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type, password, floor);
1172 aaa = (char *) malloc(strlen(roomname) + 40);
1175 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type, floor);
1177 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1184 int CtdlIPCForgetRoom(CtdlIPC * ipc, char *cret)
1189 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1194 int CtdlIPCSystemMessage(CtdlIPC * ipc, const char *message, char **mret, char *cret)
1209 aaa = (char *) malloc(strlen(message) + 6);
1213 sprintf(aaa, "MESG %s", message);
1214 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1221 int CtdlIPCNextUnvalidatedUser(CtdlIPC * ipc, char *cret)
1226 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1231 int CtdlIPCGetUserRegistration(CtdlIPC * ipc, const char *username, char **rret, char *cret)
1245 aaa = (char *) malloc(strlen(username) + 6);
1247 aaa = (char *) malloc(12);
1252 sprintf(aaa, "GREG %s", username);
1254 sprintf(aaa, "GREG _SELF_");
1255 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1262 int CtdlIPCValidateUser(CtdlIPC * ipc, const char *username, int axlevel, char *cret)
1271 if (axlevel < AxDeleted || axlevel > AxAideU)
1274 aaa = (char *) malloc(strlen(username) + 17);
1278 sprintf(aaa, "VALI %s|%d", username, axlevel);
1279 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1286 int CtdlIPCSetRoomInfo(CtdlIPC * ipc, int for_real, const char *info, char *cret)
1295 sprintf(aaa, "EINF %d", for_real);
1296 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1301 int CtdlIPCUserListing(CtdlIPC * ipc, char *searchstring, char **listing, char *cret)
1316 cmd = malloc(strlen(searchstring) + 10);
1317 sprintf(cmd, "LIST %s", searchstring);
1319 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1326 int CtdlIPCSetRegistration(CtdlIPC * ipc, const char *info, char *cret)
1333 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info), NULL, NULL, cret);
1338 int CtdlIPCMiscCheck(CtdlIPC * ipc, struct ctdlipcmisc *chek, char *cret)
1347 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1348 if (ret / 100 == 2) {
1349 chek->newmail = extract_long(cret, 0);
1350 chek->needregis = extract_int(cret, 1);
1351 chek->needvalid = extract_int(cret, 2);
1358 int CtdlIPCDeleteFile(CtdlIPC * ipc, const char *filename, char *cret)
1368 aaa = (char *) malloc(strlen(filename) + 6);
1372 sprintf(aaa, "DELF %s", filename);
1373 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1380 int CtdlIPCMoveFile(CtdlIPC * ipc, const char *filename, const char *destroom, char *cret)
1392 aaa = (char *) malloc(strlen(filename) + strlen(destroom) + 7);
1396 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1397 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1404 int CtdlIPCOnlineUsers(CtdlIPC * ipc, char **listing, time_t * stamp, char *cret)
1416 *stamp = CtdlIPCServerTime(ipc, cret);
1418 *stamp = time(NULL);
1419 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1425 int CtdlIPCFileDownload(CtdlIPC * ipc, const char *filename, void **buf, size_t resume, void (*progress_gauge_callback)
1426 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1442 if (ipc->downloading)
1445 aaa = (char *) malloc(strlen(filename) + 6);
1449 sprintf(aaa, "OPEN %s", filename);
1450 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1452 if (ret / 100 == 2) {
1453 ipc->downloading = 1;
1454 bytes = extract_long(cret, 0);
1455 last_mod = extract_int(cret, 1);
1456 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1458 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret);
1460 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1461 progress_gauge_callback, cret);
1464 ret = CtdlIPCEndDownload(ipc, cret);
1466 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1473 int CtdlIPCAttachmentDownload(CtdlIPC * ipc, long msgnum, const char *part, void **buf, void (*progress_gauge_callback)
1474 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1493 if (ipc->downloading)
1496 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1497 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1498 if (ret / 100 == 2) {
1499 ipc->downloading = 1;
1500 bytes = extract_long(cret, 0);
1501 last_mod = extract_int(cret, 1);
1502 extract_token(filename, cret, 2, '|', sizeof filename);
1503 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1504 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1505 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1506 ret = CtdlIPCEndDownload(ipc, cret);
1508 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1515 int CtdlIPCImageDownload(CtdlIPC * ipc, const char *filename, void **buf, void (*progress_gauge_callback)
1516 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1532 if (ipc->downloading)
1535 aaa = (char *) malloc(strlen(filename) + 6);
1539 sprintf(aaa, "OIMG %s", filename);
1540 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1542 if (ret / 100 == 2) {
1543 ipc->downloading = 1;
1544 bytes = extract_long(cret, 0);
1545 last_mod = extract_int(cret, 1);
1546 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1547 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1548 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1549 ret = CtdlIPCEndDownload(ipc, cret);
1551 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1558 int CtdlIPCFileUpload(CtdlIPC * ipc, const char *save_as, const char *comment, const char *path, void (*progress_gauge_callback)
1559 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1564 char MimeTestBuf[64];
1565 const char *MimeType;
1581 uploadFP = fopen(path, "r");
1585 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1590 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1591 aaa = (char *) malloc(strlen(save_as) + strlen(MimeType) + strlen(comment) + 7);
1595 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1596 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1598 if (ret / 100 == 2) {
1600 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1601 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1609 int CtdlIPCImageUpload(CtdlIPC * ipc, int for_real, const char *path, const char *save_as, void (*progress_gauge_callback)
1610 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1615 char MimeTestBuf[64];
1616 const char *MimeType;
1623 if (!path && for_real)
1625 if (!*path && for_real)
1630 aaa = (char *) malloc(strlen(save_as) + 17);
1634 uploadFP = fopen(path, "r");
1638 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1642 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1644 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1645 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1647 if (ret / 100 == 2 && for_real) {
1649 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1650 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1658 int CtdlIPCQueryUsername(CtdlIPC * ipc, const char *username, char *cret)
1668 aaa = (char *) malloc(strlen(username) + 6);
1672 sprintf(aaa, "QUSR %s", username);
1673 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1680 int CtdlIPCFloorListing(CtdlIPC * ipc, char **listing, char *cret)
1691 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1696 int CtdlIPCCreateFloor(CtdlIPC * ipc, int for_real, const char *name, char *cret)
1706 sprintf(aaa, "CFLR %s|%d", name, for_real);
1707 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1713 int CtdlIPCDeleteFloor(CtdlIPC * ipc, int for_real, int floornum, char *cret)
1722 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1723 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1728 int CtdlIPCEditFloor(CtdlIPC * ipc, int floornum, const char *floorname, char *cret)
1740 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1741 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1749 * You only need to fill out hostname, the defaults will be used if any of the
1750 * other fields are not set properly.
1752 int CtdlIPCIdentifySoftware(CtdlIPC * ipc, int developerid, int clientid,
1753 int revision, const char *software_name, const char *hostname, char *cret)
1758 if (developerid < 0 || clientid < 0 || revision < 0 || !software_name) {
1761 revision = CLIENT_VERSION - 600;
1762 software_name = "Citadel (libcitadel)";
1767 aaa = (char *) malloc(strlen(software_name) + strlen(hostname) + 29);
1771 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid, revision, software_name, hostname);
1772 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1779 int CtdlIPCSendInstantMessage(CtdlIPC * ipc, const char *username, const char *text, char *cret)
1789 aaa = (char *) malloc(strlen(username) + 8);
1794 sprintf(aaa, "SEXP %s|-", username);
1795 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1797 sprintf(aaa, "SEXP %s||", username);
1798 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1806 int CtdlIPCGetInstantMessage(CtdlIPC * ipc, char **listing, char *cret)
1817 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1822 /* mode is 0 = enable, 1 = disable, 2 = status */
1823 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC * ipc, int mode, char *cret)
1830 sprintf(aaa, "DEXP %d", mode);
1831 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1836 int CtdlIPCSetBio(CtdlIPC * ipc, char *bio, char *cret)
1843 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio), NULL, NULL, cret);
1848 int CtdlIPCGetBio(CtdlIPC * ipc, const char *username, char **listing, char *cret)
1863 aaa = (char *) malloc(strlen(username) + 6);
1867 sprintf(aaa, "RBIO %s", username);
1868 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1875 int CtdlIPCListUsersWithBios(CtdlIPC * ipc, char **listing, char *cret)
1886 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1891 int CtdlIPCStealthMode(CtdlIPC * ipc, int mode, char *cret)
1898 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1899 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1904 int CtdlIPCTerminateSession(CtdlIPC * ipc, int sid, char *cret)
1911 sprintf(aaa, "TERM %d", sid);
1912 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1917 int CtdlIPCTerminateServerNow(CtdlIPC * ipc, char *cret)
1922 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1927 int CtdlIPCTerminateServerScheduled(CtdlIPC * ipc, int mode, char *cret)
1934 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1935 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1940 int CtdlIPCEnterSystemMessage(CtdlIPC * ipc, const char *filename, const char *text, char *cret)
1952 aaa = (char *) malloc(strlen(filename) + 6);
1956 sprintf(aaa, "EMSG %s", filename);
1957 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1964 /* This function returns the actual server time reported, or 0 if error */
1965 time_t CtdlIPCServerTime(CtdlIPC * ipc, char *cret)
1970 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1971 if (ret / 100 == 2) {
1972 tret = extract_long(cret, 0);
1981 int CtdlIPCAideGetUserParameters(CtdlIPC * ipc, const char *who, struct ctdluser **uret, char *cret)
1991 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
1995 sprintf(aaa, "AGUP %s", who);
1996 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1998 if (ret / 100 == 2) {
1999 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
2000 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
2001 uret[0]->flags = extract_int(cret, 2);
2002 uret[0]->timescalled = extract_long(cret, 3);
2003 uret[0]->posted = extract_long(cret, 4);
2004 uret[0]->axlevel = extract_int(cret, 5);
2005 uret[0]->usernum = extract_long(cret, 6);
2006 uret[0]->lastcall = extract_long(cret, 7);
2007 uret[0]->USuserpurge = extract_int(cret, 8);
2014 int CtdlIPCAideSetUserParameters(CtdlIPC * ipc, const struct ctdluser *uret, char *cret)
2024 aaa = (char *) malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
2028 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
2029 uret->fullname, uret->password, uret->flags, uret->timescalled,
2030 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge);
2031 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2038 int CtdlIPCAideGetEmailAddresses(CtdlIPC * ipc, const char *who, char *target_buf, char *cret)
2042 char *emailaddrs = NULL;
2043 size_t emailaddrs_len = 0;
2045 sprintf(aaa, "AGEA %s", who);
2046 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
2048 if (ret / 100 == 1) {
2049 strcpy(target_buf, emailaddrs);
2052 if (emailaddrs != NULL) {
2061 int CtdlIPCAideSetEmailAddresses(CtdlIPC * ipc, const char *who, char *emailaddrs, char *cret)
2073 sprintf(aaa, "ASEA %s", who);
2074 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2080 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2081 /* caller must free the struct ExpirePolicy */
2082 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC * ipc, GPEXWhichPolicy which, struct ExpirePolicy **policy, char *cret)
2084 static char *proto[] = {
2088 strof(mailboxespolicy)
2098 *policy = (struct ExpirePolicy *) calloc(1, sizeof(struct ExpirePolicy));
2101 if (which < 0 || which > 3)
2104 sprintf(cmd, "GPEX %s", proto[which]);
2105 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2106 if (ret / 100 == 2) {
2107 policy[0]->expire_mode = extract_int(cret, 0);
2108 policy[0]->expire_value = extract_int(cret, 1);
2115 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2116 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2117 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC * ipc, int which, struct ExpirePolicy *policy, char *cret)
2120 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2124 if (which < 0 || which > 3)
2128 if (policy->expire_mode < 0 || policy->expire_mode > 3)
2130 if (policy->expire_mode >= 2 && policy->expire_value < 1)
2133 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which], policy->expire_mode, policy->expire_value);
2134 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2139 int CtdlIPCGetSystemConfig(CtdlIPC * ipc, char **listing, char *cret)
2150 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0, listing, &bytes, cret);
2155 int CtdlIPCSetSystemConfig(CtdlIPC * ipc, const char *listing, char *cret)
2162 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing), NULL, NULL, cret);
2167 int CtdlIPCGetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, char **listing, char *cret)
2182 aaa = malloc(strlen(mimetype) + 13);
2185 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2186 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2193 int CtdlIPCSetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, const char *listing, char *cret)
2205 aaa = malloc(strlen(mimetype) + 13);
2208 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2209 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2216 int CtdlIPCGetRoomNetworkConfig(CtdlIPC * ipc, char **listing, char *cret)
2227 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2232 int CtdlIPCSetRoomNetworkConfig(CtdlIPC * ipc, const char *listing, char *cret)
2239 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2244 int CtdlIPCRequestClientLogout(CtdlIPC * ipc, int session, char *cret)
2253 sprintf(aaa, "REQT %d", session);
2254 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2259 int CtdlIPCSetMessageSeen(CtdlIPC * ipc, long msgnum, int seen, char *cret)
2268 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2269 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2274 int CtdlIPCStartEncryption(CtdlIPC * ipc, char *cret)
2283 /* New SSL object */
2284 temp_ssl = SSL_new(ssl_ctx);
2286 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2289 /* Pointless flag waving */
2290 #if SSLEAY_VERSION_NUMBER >= 0x0922
2291 SSL_set_session_id_context(temp_ssl, (const unsigned char *) "Citadel SID", 14);
2294 /* Associate network connection with SSL object */
2295 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2296 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2300 if (status_hook != NULL) {
2301 status_hook("Requesting encryption...\r");
2304 /* Ready to start SSL/TLS */
2305 r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2307 error_printf("Server can't start TLS: %s\n", buf);
2312 /* Do SSL/TLS handshake */
2313 if ((a = SSL_connect(temp_ssl)) < 1) {
2314 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2318 ipc->ssl = temp_ssl;
2320 error_printf("Encrypting with %s cipher %s\n",
2321 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)), SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2326 #endif /* HAVE_OPENSSL */
2331 static void endtls(SSL * ssl)
2342 int CtdlIPCDirectoryLookup(CtdlIPC * ipc, const char *address, char *cret)
2352 aaa = (char *) malloc(strlen(address) + 6);
2356 sprintf(aaa, "QDIR %s", address);
2357 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2364 int CtdlIPCInternalProgram(CtdlIPC * ipc, int secret, char *cret)
2370 sprintf(aaa, "IPGM %d", secret);
2371 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2377 /* ************************************************************************** */
2378 /* Stuff below this line is not for public consumption */
2379 /* ************************************************************************** */
2382 /* Read a listing from the server up to 000. Append to dest if it exists */
2383 char *CtdlIPCReadListing(CtdlIPC * ipc, char *dest)
2392 length = strlen(ret);
2397 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2398 linelength = strlen(aaa);
2399 ret = (char *) realloc(ret, (size_t) (length + linelength + 2));
2401 strcpy(&ret[length], aaa);
2402 length += linelength;
2403 strcpy(&ret[length++], "\n");
2411 /* Send a listing to the server; generate the ending 000. */
2412 int CtdlIPCSendListing(CtdlIPC * ipc, const char *listing)
2416 text = (char *) malloc(strlen(listing) + 6);
2418 strcpy(text, listing);
2419 while (text[strlen(text) - 1] == '\n')
2420 text[strlen(text) - 1] = '\0';
2421 strcat(text, "\n000");
2422 CtdlIPC_putline(ipc, text);
2426 /* Malloc failed but we are committed to send */
2427 /* This may result in extra blanks at the bottom */
2428 CtdlIPC_putline(ipc, text);
2429 CtdlIPC_putline(ipc, "000");
2435 /* Partial read of file from server */
2436 size_t CtdlIPCPartialRead(CtdlIPC * ipc, void **buf, size_t offset, size_t bytes, char *cret)
2449 sprintf(aaa, "READ %d|%d", (int) offset, (int) bytes);
2450 CtdlIPC_putline(ipc, aaa);
2451 CtdlIPC_getline(ipc, aaa);
2453 strcpy(cret, &aaa[4]);
2455 len = extract_long(&aaa[4], 0);
2456 *buf = (void *) realloc(*buf, (size_t) (offset + len));
2458 /* I know what I'm doing */
2459 serv_read(ipc, ((char *) (*buf) + offset), len);
2461 /* We have to read regardless */
2462 serv_read(ipc, aaa, len);
2466 CtdlIPC_unlock(ipc);
2472 int CtdlIPCEndDownload(CtdlIPC * ipc, char *cret)
2478 if (!ipc->downloading)
2481 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2483 ipc->downloading = 0;
2489 int CtdlIPCSpecifyPreferredFormats(CtdlIPC * ipc, char *cret, char *formats)
2494 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2495 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2502 int CtdlIPCReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2503 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2513 if (!ipc->downloading)
2517 if (progress_gauge_callback)
2518 progress_gauge_callback(ipc, len, bytes);
2519 while (len < bytes) {
2522 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2528 if (progress_gauge_callback)
2529 progress_gauge_callback(ipc, len, bytes);
2534 /* READ - pipelined */
2535 int CtdlIPCHighSpeedReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2536 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2539 int calls; /* How many calls in the pipeline */
2540 int i; /* iterator */
2549 if (!ipc->downloading)
2552 *buf = (void *) realloc(*buf, bytes - resume);
2558 if (progress_gauge_callback)
2559 progress_gauge_callback(ipc, len, bytes);
2561 /* How many calls will be in the pipeline? */
2562 calls = (bytes - resume) / 4096;
2563 if ((bytes - resume) % 4096)
2566 /* Send all requests at once */
2567 for (i = 0; i < calls; i++) {
2568 sprintf(aaa, "READ %d|4096", (int) (i * 4096 + resume));
2569 CtdlIPC_putline(ipc, aaa);
2572 /* Receive all responses at once */
2573 for (i = 0; i < calls; i++) {
2574 CtdlIPC_getline(ipc, aaa);
2576 strcpy(cret, &aaa[4]);
2578 len = extract_long(&aaa[4], 0);
2579 /* I know what I'm doing */
2580 serv_read(ipc, ((char *) (*buf) + (i * 4096)), len);
2582 if (progress_gauge_callback)
2583 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2585 CtdlIPC_unlock(ipc);
2591 int CtdlIPCEndUpload(CtdlIPC * ipc, int discard, char *cret)
2598 if (!ipc->uploading)
2601 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2602 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2609 int CtdlIPCWriteUpload(CtdlIPC * ipc, FILE * uploadFP, void (*progress_gauge_callback)
2610 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2617 FILE *fd = uploadFP;
2623 fseek(fd, 0L, SEEK_END);
2627 if (progress_gauge_callback)
2628 progress_gauge_callback(ipc, 0, bytes);
2630 while (offset < bytes) {
2633 /* Read some data in */
2634 to_write = fread(buf, 1, 4096, fd);
2636 if (feof(fd) || ferror(fd))
2639 sprintf(aaa, "WRIT %d", (int) to_write);
2640 CtdlIPC_putline(ipc, aaa);
2641 CtdlIPC_getline(ipc, aaa);
2642 strcpy(cret, &aaa[4]);
2644 if (aaa[0] == '7') {
2645 to_write = extract_long(&aaa[4], 0);
2647 serv_write(ipc, buf, to_write);
2649 if (progress_gauge_callback)
2650 progress_gauge_callback(ipc, offset, bytes);
2651 /* Detect short reads and back up if needed */
2652 /* offset will never be negative anyway */
2653 fseek(fd, (signed) offset, SEEK_SET);
2658 if (progress_gauge_callback)
2659 progress_gauge_callback(ipc, 1, 1);
2662 return (!ferr ? ret : -2);
2667 * Generic command method. This method should handle any server command
2668 * except for CHAT. It takes the following arguments:
2670 * ipc The server to speak with
2671 * command Preformatted command to send to server
2672 * to_send A text or binary file to send to server
2673 * (only sent if server requests it)
2674 * bytes_to_send The number of bytes in to_send (required if
2675 * sending binary, optional if sending listing)
2676 * to_receive Pointer to a NULL pointer, if the server
2677 * sends text or binary we will allocate memory
2678 * for the file and stuff it here
2679 * bytes_to_receive If a file is received, we will store its
2681 * proto_response The protocol response. Caller must provide
2682 * this buffer and ensure that it is at least
2683 * 128 bytes in length.
2685 * This function returns a number equal to the protocol response number,
2686 * -1 if an internal error occurred, -2 if caller provided bad values,
2687 * or 0 - the protocol response number if bad values were found during
2688 * the protocol exchange.
2689 * It stores the protocol response string (minus the number) in
2690 * protocol_response as described above. Some commands send additional
2691 * data in this string.
2693 int CtdlIPCGenericCommand(CtdlIPC * ipc,
2694 const char *command, const char *to_send,
2695 size_t bytes_to_send, char **to_receive, size_t * bytes_to_receive, char *proto_response)
2702 if (!proto_response)
2706 CtdlIPC_putline(ipc, command);
2708 CtdlIPC_getline(ipc, proto_response);
2709 if (proto_response[3] == '*')
2711 ret = atoi(proto_response);
2712 strcpy(proto_response, &proto_response[4]);
2713 switch (ret / 100) {
2714 default: /* Unknown, punt */
2716 case 3: /* MORE_DATA */
2718 /* Don't need to do anything */
2720 case 1: /* LISTING_FOLLOWS */
2721 if (to_receive && !*to_receive && bytes_to_receive) {
2722 *to_receive = CtdlIPCReadListing(ipc, NULL);
2723 } else { /* Drain */
2724 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2728 case 4: /* SEND_LISTING */
2730 CtdlIPCSendListing(ipc, to_send);
2732 /* No listing given, fake it */
2733 CtdlIPC_putline(ipc, "000");
2737 case 6: /* BINARY_FOLLOWS */
2738 if (to_receive && !*to_receive && bytes_to_receive) {
2739 *bytes_to_receive = extract_long(proto_response, 0);
2740 *to_receive = (char *)
2741 malloc((size_t) * bytes_to_receive);
2745 serv_read(ipc, *to_receive, *bytes_to_receive);
2751 drain = extract_long(proto_response, 0);
2752 while (drain > SIZ) {
2753 serv_read(ipc, buf, SIZ);
2756 serv_read(ipc, buf, drain);
2760 case 7: /* SEND_BINARY */
2761 if (to_send && bytes_to_send) {
2762 serv_write(ipc, to_send, bytes_to_send);
2763 } else if (bytes_to_send) {
2764 /* Fake it, send nulls */
2767 fake = bytes_to_send;
2768 memset(buf, '\0', SIZ);
2769 while (fake > SIZ) {
2770 serv_write(ipc, buf, SIZ);
2773 serv_write(ipc, buf, fake);
2775 } /* else who knows? DANGER WILL ROBINSON */
2777 case 8: /* START_CHAT_MODE */
2778 if (!strncasecmp(command, "CHAT", 4)) {
2779 /* Don't call chatmode with generic! */
2780 CtdlIPC_putline(ipc, "/quit");
2783 /* In this mode we send then receive listing */
2785 CtdlIPCSendListing(ipc, to_send);
2787 /* No listing given, fake it */
2788 CtdlIPC_putline(ipc, "000");
2791 if (to_receive && !*to_receive && bytes_to_receive) {
2792 *to_receive = CtdlIPCReadListing(ipc, NULL);
2793 } else { /* Drain */
2794 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2799 case 9: /* ASYNC_MSG */
2800 /* CtdlIPCDoAsync(ret, proto_response); */
2801 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2807 CtdlIPC_unlock(ipc);
2813 * Connect to a Citadel on a remote host using a TCP/IP socket
2815 static int tcp_connectsock(char *host, char *service)
2817 struct in6_addr serveraddr;
2818 struct addrinfo hints;
2819 struct addrinfo *res = NULL;
2820 struct addrinfo *ai = NULL;
2824 if ((host == NULL) || IsEmptyStr(host)) {
2825 service = DEFAULT_HOST;
2827 if ((service == NULL) || IsEmptyStr(service)) {
2828 service = DEFAULT_PORT;
2831 memset(&hints, 0x00, sizeof(hints));
2832 hints.ai_flags = AI_NUMERICSERV;
2833 hints.ai_family = AF_UNSPEC;
2834 hints.ai_socktype = SOCK_STREAM;
2837 * Handle numeric IPv4 and IPv6 addresses
2839 rc = inet_pton(AF_INET, host, &serveraddr);
2840 if (rc == 1) { /* dotted quad */
2841 hints.ai_family = AF_INET;
2842 hints.ai_flags |= AI_NUMERICHOST;
2844 rc = inet_pton(AF_INET6, host, &serveraddr);
2845 if (rc == 1) { /* IPv6 address */
2846 hints.ai_family = AF_INET6;
2847 hints.ai_flags |= AI_NUMERICHOST;
2851 /* Begin the connection process */
2853 rc = getaddrinfo(host, service, &hints, &res);
2859 * Try all available addresses until we connect to one or until we run out.
2861 for (ai = res; ai != NULL; ai = ai->ai_next) {
2862 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2866 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2868 return (sock); /* Connected! */
2870 close(sock); /* Failed. Close the socket to avoid fd leak! */
2882 * Connect to a Citadel on the local host using a unix domain socket
2884 static int uds_connectsock(int *isLocal, char *sockpath)
2886 struct sockaddr_un addr;
2889 memset(&addr, 0, sizeof(addr));
2890 addr.sun_family = AF_UNIX;
2891 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2893 s = socket(AF_UNIX, SOCK_STREAM, 0);
2898 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2909 * input binary data from socket
2911 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes)
2913 unsigned int len, rlen;
2915 #if defined(HAVE_OPENSSL)
2917 serv_read_ssl(ipc, buf, bytes);
2922 while (len < bytes) {
2923 rlen = read(ipc->sock, &buf[len], bytes - len);
2925 connection_died(ipc, 0);
2934 * send binary to server
2936 void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
2938 unsigned int bytes_written = 0;
2941 #if defined(HAVE_OPENSSL)
2943 serv_write_ssl(ipc, buf, nbytes);
2947 while (bytes_written < nbytes) {
2948 retval = write(ipc->sock, &buf[bytes_written], nbytes - bytes_written);
2950 connection_died(ipc, 0);
2953 bytes_written += retval;
2960 * input binary data from encrypted connection
2962 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes)
2968 while (len < bytes) {
2969 if (SSL_want_read(ipc->ssl)) {
2970 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2971 error_printf("SSL_write in serv_read:\n");
2972 ERR_print_errors_fp(stderr);
2975 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2979 errval = SSL_get_error(ipc->ssl, rlen);
2980 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
2985 Not sure why we'd want to handle these error codes any differently,
2986 but this definitely isn't the way to handle them. Someone must have
2987 naively assumed that we could fall back to unencrypted communications,
2988 but all it does is just recursively blow the stack.
2989 if (errval == SSL_ERROR_ZERO_RETURN ||
2990 errval == SSL_ERROR_SSL) {
2991 serv_read(ipc, &buf[len], bytes - len);
2995 error_printf("SSL_read in serv_read: %s\n", ERR_reason_error_string(ERR_peek_error()));
2996 connection_died(ipc, 1);
3005 * send binary to server encrypted
3007 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
3009 unsigned int bytes_written = 0;
3013 while (bytes_written < nbytes) {
3014 if (SSL_want_write(ipc->ssl)) {
3015 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
3016 error_printf("SSL_read in serv_write:\n");
3017 ERR_print_errors_fp(stderr);
3020 retval = SSL_write(ipc->ssl, &buf[bytes_written], nbytes - bytes_written);
3024 errval = SSL_get_error(ipc->ssl, retval);
3025 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
3029 if (errval == SSL_ERROR_ZERO_RETURN || errval == SSL_ERROR_SSL) {
3030 serv_write(ipc, &buf[bytes_written], nbytes - bytes_written);
3033 error_printf("SSL_write in serv_write: %s\n", ERR_reason_error_string(ERR_peek_error()));
3034 connection_died(ipc, 1);
3037 bytes_written += retval;
3044 static void CtdlIPC_init_OpenSSL(void)
3047 const SSL_METHOD *ssl_method;
3050 /* already done init */
3059 SSL_load_error_strings();
3060 SSLeay_add_ssl_algorithms();
3062 /* Set up the SSL context in which we will oeprate */
3063 ssl_method = SSLv23_client_method();
3064 ssl_ctx = SSL_CTX_new(ssl_method);
3066 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
3069 /* Any reasonable cipher we can get */
3070 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
3071 error_printf("No ciphers available for encryption\n");
3074 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
3076 /* Load DH parameters into the context */
3079 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
3083 if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
3084 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
3089 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
3093 #endif /* HAVE_OPENSSL */
3096 int ReadNetworkChunk(CtdlIPC * ipc)
3111 FD_SET(ipc->sock, &read_fd);
3112 ret = select(ipc->sock + 1, &read_fd, NULL, NULL, &tv);
3116 *(ipc->BufPtr) = '\0';
3117 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3119 ipc->BufPtr[n] = '\0';
3124 } else if (ret < 0) {
3125 if (!(errno == EINTR || errno == EAGAIN))
3126 error_printf("\nselect failed: %d %s\n", err, strerror(err));
3132 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3134 ipc->BufPtr[n]='\0';
3139 connection_died(ipc, 0);
3147 * input string from socket - implemented in terms of serv_read()
3151 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3154 char *aptr, *bptr, *aeptr, *beptr;
3156 // error_printf("---\n");
3159 #if defined(HAVE_OPENSSL)
3162 /* Read one character at a time. */
3164 serv_read(ipc, &buf[i], 1);
3165 if (buf[i] == '\n' || i == (SIZ - 1))
3169 /* If we got a long line, discard characters until the newline. */
3171 while (buf[i] != '\n')
3172 serv_read(ipc, &buf[i], 1);
3174 /* Strip the trailing newline (and carriage return, if present) */
3175 if (i >= 0 && buf[i] == 10)
3177 if (i >= 0 && buf[i] == 13)
3182 if (ipc->Buf == NULL) {
3184 ipc->Buf = (char *) malloc(ipc->BufSize + 10);
3186 ipc->BufPtr = ipc->Buf;
3190 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3191 if (ipc->BufUsed == 0)
3192 ReadNetworkChunk(ipc);
3194 //// if (ipc->BufUsed != 0) while (1)
3199 aeptr = ipc->Buf + ipc->BufSize;
3200 while ((aptr < aeptr) && (bptr < beptr) && (*aptr != '\0') && (*aptr != '\n'))
3201 *(bptr++) = *(aptr++);
3202 if ((*aptr == '\n') && (aptr < aeptr)) {
3203 /* Terminate it right, remove the line breaks */
3204 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3206 while ((aptr < aeptr) && (*(aptr + 1) == '\0'))
3209 // 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);
3210 if ((bptr > buf + 1) && (*(bptr - 1) == '\r'))
3213 /* is there more in the buffer we need to read later? */
3214 if (ipc->Buf + ipc->BufUsed > aptr) {
3218 ipc->BufPtr = ipc->Buf;
3220 // error_printf("----bla6\n");
3223 } /* should we move our read stuf to the bufferstart so we have more space at the end? */
3224 else if ((ipc->BufPtr != ipc->Buf) && (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4)))) {
3225 size_t NewBufSize = ipc->BufSize * 2;
3226 int delta = (ipc->BufPtr - ipc->Buf);
3229 /* if the line would end after our buffer, we should use a bigger buffer. */
3230 NewBuf = (char *) malloc(NewBufSize + 10);
3231 memcpy(NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3233 ipc->Buf = ipc->BufPtr = NewBuf;
3234 ipc->BufUsed -= delta;
3235 ipc->BufSize = NewBufSize;
3237 if (ReadNetworkChunk(ipc) < 0) {
3238 // error_printf("----bla\n");
3242 /// error_printf("----bl45761%s\nipc->BufUsed");
3244 // error_printf("----bla1\n");
3247 #else /* CHUNKED_READ */
3249 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3253 /* Read one character at a time. */
3255 serv_read(ipc, &buf[i], 1);
3256 if (buf[i] == '\n' || i == (SIZ - 1))
3260 /* If we got a long line, discard characters until the newline. */
3262 while (buf[i] != '\n')
3263 serv_read(ipc, &buf[i], 1);
3265 /* Strip the trailing newline (and carriage return, if present) */
3266 if (i >= 0 && buf[i] == 10)
3268 if (i >= 0 && buf[i] == 13)
3273 #endif /* CHUNKED_READ */
3276 void CtdlIPC_chat_recv(CtdlIPC * ipc, char *buf)
3278 CtdlIPC_getline(ipc, buf);
3282 * send line to server - implemented in terms of serv_write()
3284 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf)
3290 cmd = malloc(len + 2);
3292 /* This requires no extra memory */
3293 serv_write(ipc, buf, len);
3294 serv_write(ipc, "\n", 1);
3296 /* This is network-optimized */
3297 strncpy(cmd, buf, len);
3298 strcpy(cmd + len, "\n");
3299 serv_write(ipc, cmd, len + 1);
3303 ipc->last_command_sent = time(NULL);
3306 void CtdlIPC_chat_send(CtdlIPC * ipc, const char *buf)
3308 CtdlIPC_putline(ipc, buf);
3315 CtdlIPC *CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3323 ipc = malloc(sizeof(struct _CtdlIPC));
3327 #if defined(HAVE_OPENSSL)
3329 CtdlIPC_init_OpenSSL();
3331 ipc->sock = -1; /* Not connected */
3332 ipc->isLocal = 0; /* Not local, of course! */
3333 ipc->downloading = 0;
3335 ipc->last_command_sent = 0L;
3336 ipc->network_status_cb = NULL;
3341 strcpy(cithost, DEFAULT_HOST); /* default host */
3342 strcpy(citport, DEFAULT_PORT); /* default port */
3344 /* Allow caller to supply our values */
3345 if (hostbuf && strlen(hostbuf) > 0) {
3346 strcpy(cithost, hostbuf);
3348 if (portbuf && strlen(portbuf) > 0) {
3349 strcpy(citport, portbuf);
3352 /* Read host/port from command line if present */
3353 for (a = 0; a < argc; ++a) {
3356 } else if (a == 1) {
3357 strcpy(cithost, argv[a]);
3358 } else if (a == 2) {
3359 strcpy(citport, argv[a]);
3361 error_printf("%s: usage: ", argv[0]);
3362 error_printf("%s [host] [port] ", argv[0]);
3369 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3373 /* If we're using a unix domain socket we can do a bunch of stuff */
3374 if (!strcmp(cithost, UDS)) {
3375 if (!strcasecmp(citport, DEFAULT_PORT)) {
3376 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3378 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3380 printf("[%s]\n", sockpath);
3381 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3382 if (ipc->sock == -1) {
3386 if (hostbuf != NULL)
3387 strcpy(hostbuf, cithost);
3388 if (portbuf != NULL)
3389 strcpy(portbuf, sockpath);
3390 strcpy(ipc->ip_hostname, "");
3391 strcpy(ipc->ip_address, "");
3395 printf("[%s:%s]\n", cithost, citport);
3396 ipc->sock = tcp_connectsock(cithost, citport);
3397 if (ipc->sock == -1) {
3403 /* Learn the actual network identity of the host to which we are connected */
3405 struct sockaddr_in6 clientaddr;
3406 unsigned int addrlen = sizeof(clientaddr);
3408 ipc->ip_hostname[0] = 0;
3409 ipc->ip_address[0] = 0;
3411 getpeername(ipc->sock, (struct sockaddr *) &clientaddr, &addrlen);
3412 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0);
3413 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST);
3415 /* stuff other things elsewhere */
3417 if (hostbuf != NULL)
3418 strcpy(hostbuf, cithost);
3419 if (portbuf != NULL)
3420 strcpy(portbuf, citport);
3426 * Disconnect and delete the IPC class (destructor)
3428 void CtdlIPC_delete(CtdlIPC * ipc)
3432 SSL_shutdown(ipc->ssl);
3437 if (ipc->sock > -1) {
3438 shutdown(ipc->sock, 2); /* Close it up */
3441 if (ipc->Buf != NULL)
3450 * Disconnect and delete the IPC class (destructor)
3451 * Also NULLs out the pointer
3453 void CtdlIPC_delete_ptr(CtdlIPC ** pipc)
3455 CtdlIPC_delete(*pipc);
3461 * return the file descriptor of the server socket so we can select() on it.
3463 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3466 int CtdlIPC_getsockfd(CtdlIPC * ipc)
3473 * return one character
3475 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3478 char CtdlIPC_get(CtdlIPC * ipc)
3483 serv_read(ipc, buf, 1);