2 * Copyright (c) 1987-2018 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 CtdlIPCChangeHostname(CtdlIPC * ipc, const char *hostname, char *cret)
1971 aaa = (char *) malloc(strlen(hostname) + 6);
1975 sprintf(aaa, "HCHG %s", hostname);
1976 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1983 int CtdlIPCChangeRoomname(CtdlIPC * ipc, const char *roomname, char *cret)
1993 aaa = (char *) malloc(strlen(roomname) + 6);
1997 sprintf(aaa, "RCHG %s", roomname);
1998 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2005 int CtdlIPCChangeUsername(CtdlIPC * ipc, const char *username, char *cret)
2015 aaa = (char *) malloc(strlen(username) + 6);
2019 sprintf(aaa, "UCHG %s", username);
2020 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2027 /* This function returns the actual server time reported, or 0 if error */
2028 time_t CtdlIPCServerTime(CtdlIPC * ipc, char *cret)
2033 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
2034 if (ret / 100 == 2) {
2035 tret = extract_long(cret, 0);
2044 int CtdlIPCAideGetUserParameters(CtdlIPC * ipc, const char *who, struct ctdluser **uret, char *cret)
2054 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
2058 sprintf(aaa, "AGUP %s", who);
2059 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2061 if (ret / 100 == 2) {
2062 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
2063 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
2064 uret[0]->flags = extract_int(cret, 2);
2065 uret[0]->timescalled = extract_long(cret, 3);
2066 uret[0]->posted = extract_long(cret, 4);
2067 uret[0]->axlevel = extract_int(cret, 5);
2068 uret[0]->usernum = extract_long(cret, 6);
2069 uret[0]->lastcall = extract_long(cret, 7);
2070 uret[0]->USuserpurge = extract_int(cret, 8);
2077 int CtdlIPCAideSetUserParameters(CtdlIPC * ipc, const struct ctdluser *uret, char *cret)
2087 aaa = (char *) malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
2091 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
2092 uret->fullname, uret->password, uret->flags, uret->timescalled,
2093 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge);
2094 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2101 int CtdlIPCAideGetEmailAddresses(CtdlIPC * ipc, const char *who, char *target_buf, char *cret)
2105 char *emailaddrs = NULL;
2106 size_t emailaddrs_len = 0;
2108 sprintf(aaa, "AGEA %s", who);
2109 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
2111 if (ret / 100 == 1) {
2112 strcpy(target_buf, emailaddrs);
2115 if (emailaddrs != NULL) {
2124 int CtdlIPCAideSetEmailAddresses(CtdlIPC * ipc, const char *who, char *emailaddrs, char *cret)
2136 sprintf(aaa, "ASEA %s", who);
2137 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2143 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2144 /* caller must free the struct ExpirePolicy */
2145 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC * ipc, GPEXWhichPolicy which, struct ExpirePolicy **policy, char *cret)
2147 static char *proto[] = {
2151 strof(mailboxespolicy)
2161 *policy = (struct ExpirePolicy *) calloc(1, sizeof(struct ExpirePolicy));
2164 if (which < 0 || which > 3)
2167 sprintf(cmd, "GPEX %s", proto[which]);
2168 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2169 if (ret / 100 == 2) {
2170 policy[0]->expire_mode = extract_int(cret, 0);
2171 policy[0]->expire_value = extract_int(cret, 1);
2178 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2179 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2180 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC * ipc, int which, struct ExpirePolicy *policy, char *cret)
2183 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2187 if (which < 0 || which > 3)
2191 if (policy->expire_mode < 0 || policy->expire_mode > 3)
2193 if (policy->expire_mode >= 2 && policy->expire_value < 1)
2196 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which], policy->expire_mode, policy->expire_value);
2197 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2202 int CtdlIPCGetSystemConfig(CtdlIPC * ipc, char **listing, char *cret)
2213 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0, listing, &bytes, cret);
2218 int CtdlIPCSetSystemConfig(CtdlIPC * ipc, const char *listing, char *cret)
2225 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing), NULL, NULL, cret);
2230 int CtdlIPCGetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, char **listing, char *cret)
2245 aaa = malloc(strlen(mimetype) + 13);
2248 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2249 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2256 int CtdlIPCSetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, const char *listing, char *cret)
2268 aaa = malloc(strlen(mimetype) + 13);
2271 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2272 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2279 int CtdlIPCGetRoomNetworkConfig(CtdlIPC * ipc, char **listing, char *cret)
2290 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2295 int CtdlIPCSetRoomNetworkConfig(CtdlIPC * ipc, const char *listing, char *cret)
2302 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2307 int CtdlIPCRequestClientLogout(CtdlIPC * ipc, int session, char *cret)
2316 sprintf(aaa, "REQT %d", session);
2317 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2322 int CtdlIPCSetMessageSeen(CtdlIPC * ipc, long msgnum, int seen, char *cret)
2331 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2332 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2337 int CtdlIPCStartEncryption(CtdlIPC * ipc, char *cret)
2346 /* New SSL object */
2347 temp_ssl = SSL_new(ssl_ctx);
2349 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2352 /* Pointless flag waving */
2353 #if SSLEAY_VERSION_NUMBER >= 0x0922
2354 SSL_set_session_id_context(temp_ssl, (const unsigned char *) "Citadel SID", 14);
2357 /* Associate network connection with SSL object */
2358 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2359 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2363 if (status_hook != NULL) {
2364 status_hook("Requesting encryption...\r");
2367 /* Ready to start SSL/TLS */
2368 r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2370 error_printf("Server can't start TLS: %s\n", buf);
2375 /* Do SSL/TLS handshake */
2376 if ((a = SSL_connect(temp_ssl)) < 1) {
2377 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2381 ipc->ssl = temp_ssl;
2383 error_printf("Encrypting with %s cipher %s\n",
2384 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)), SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2389 #endif /* HAVE_OPENSSL */
2394 static void endtls(SSL * ssl)
2405 int CtdlIPCDirectoryLookup(CtdlIPC * ipc, const char *address, char *cret)
2415 aaa = (char *) malloc(strlen(address) + 6);
2419 sprintf(aaa, "QDIR %s", address);
2420 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2427 int CtdlIPCInternalProgram(CtdlIPC * ipc, int secret, char *cret)
2433 sprintf(aaa, "IPGM %d", secret);
2434 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2440 /* ************************************************************************** */
2441 /* Stuff below this line is not for public consumption */
2442 /* ************************************************************************** */
2445 /* Read a listing from the server up to 000. Append to dest if it exists */
2446 char *CtdlIPCReadListing(CtdlIPC * ipc, char *dest)
2455 length = strlen(ret);
2460 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2461 linelength = strlen(aaa);
2462 ret = (char *) realloc(ret, (size_t) (length + linelength + 2));
2464 strcpy(&ret[length], aaa);
2465 length += linelength;
2466 strcpy(&ret[length++], "\n");
2474 /* Send a listing to the server; generate the ending 000. */
2475 int CtdlIPCSendListing(CtdlIPC * ipc, const char *listing)
2479 text = (char *) malloc(strlen(listing) + 6);
2481 strcpy(text, listing);
2482 while (text[strlen(text) - 1] == '\n')
2483 text[strlen(text) - 1] = '\0';
2484 strcat(text, "\n000");
2485 CtdlIPC_putline(ipc, text);
2489 /* Malloc failed but we are committed to send */
2490 /* This may result in extra blanks at the bottom */
2491 CtdlIPC_putline(ipc, text);
2492 CtdlIPC_putline(ipc, "000");
2498 /* Partial read of file from server */
2499 size_t CtdlIPCPartialRead(CtdlIPC * ipc, void **buf, size_t offset, size_t bytes, char *cret)
2512 sprintf(aaa, "READ %d|%d", (int) offset, (int) bytes);
2513 CtdlIPC_putline(ipc, aaa);
2514 CtdlIPC_getline(ipc, aaa);
2516 strcpy(cret, &aaa[4]);
2518 len = extract_long(&aaa[4], 0);
2519 *buf = (void *) realloc(*buf, (size_t) (offset + len));
2521 /* I know what I'm doing */
2522 serv_read(ipc, ((char *) (*buf) + offset), len);
2524 /* We have to read regardless */
2525 serv_read(ipc, aaa, len);
2529 CtdlIPC_unlock(ipc);
2535 int CtdlIPCEndDownload(CtdlIPC * ipc, char *cret)
2541 if (!ipc->downloading)
2544 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2546 ipc->downloading = 0;
2552 int CtdlIPCSpecifyPreferredFormats(CtdlIPC * ipc, char *cret, char *formats)
2557 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2558 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2565 int CtdlIPCReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2566 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2576 if (!ipc->downloading)
2580 if (progress_gauge_callback)
2581 progress_gauge_callback(ipc, len, bytes);
2582 while (len < bytes) {
2585 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2591 if (progress_gauge_callback)
2592 progress_gauge_callback(ipc, len, bytes);
2597 /* READ - pipelined */
2598 int CtdlIPCHighSpeedReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2599 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2602 int calls; /* How many calls in the pipeline */
2603 int i; /* iterator */
2612 if (!ipc->downloading)
2615 *buf = (void *) realloc(*buf, bytes - resume);
2621 if (progress_gauge_callback)
2622 progress_gauge_callback(ipc, len, bytes);
2624 /* How many calls will be in the pipeline? */
2625 calls = (bytes - resume) / 4096;
2626 if ((bytes - resume) % 4096)
2629 /* Send all requests at once */
2630 for (i = 0; i < calls; i++) {
2631 sprintf(aaa, "READ %d|4096", (int) (i * 4096 + resume));
2632 CtdlIPC_putline(ipc, aaa);
2635 /* Receive all responses at once */
2636 for (i = 0; i < calls; i++) {
2637 CtdlIPC_getline(ipc, aaa);
2639 strcpy(cret, &aaa[4]);
2641 len = extract_long(&aaa[4], 0);
2642 /* I know what I'm doing */
2643 serv_read(ipc, ((char *) (*buf) + (i * 4096)), len);
2645 if (progress_gauge_callback)
2646 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2648 CtdlIPC_unlock(ipc);
2654 int CtdlIPCEndUpload(CtdlIPC * ipc, int discard, char *cret)
2661 if (!ipc->uploading)
2664 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2665 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2672 int CtdlIPCWriteUpload(CtdlIPC * ipc, FILE * uploadFP, void (*progress_gauge_callback)
2673 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2680 FILE *fd = uploadFP;
2686 fseek(fd, 0L, SEEK_END);
2690 if (progress_gauge_callback)
2691 progress_gauge_callback(ipc, 0, bytes);
2693 while (offset < bytes) {
2696 /* Read some data in */
2697 to_write = fread(buf, 1, 4096, fd);
2699 if (feof(fd) || ferror(fd))
2702 sprintf(aaa, "WRIT %d", (int) to_write);
2703 CtdlIPC_putline(ipc, aaa);
2704 CtdlIPC_getline(ipc, aaa);
2705 strcpy(cret, &aaa[4]);
2707 if (aaa[0] == '7') {
2708 to_write = extract_long(&aaa[4], 0);
2710 serv_write(ipc, buf, to_write);
2712 if (progress_gauge_callback)
2713 progress_gauge_callback(ipc, offset, bytes);
2714 /* Detect short reads and back up if needed */
2715 /* offset will never be negative anyway */
2716 fseek(fd, (signed) offset, SEEK_SET);
2721 if (progress_gauge_callback)
2722 progress_gauge_callback(ipc, 1, 1);
2725 return (!ferr ? ret : -2);
2730 * Generic command method. This method should handle any server command
2731 * except for CHAT. It takes the following arguments:
2733 * ipc The server to speak with
2734 * command Preformatted command to send to server
2735 * to_send A text or binary file to send to server
2736 * (only sent if server requests it)
2737 * bytes_to_send The number of bytes in to_send (required if
2738 * sending binary, optional if sending listing)
2739 * to_receive Pointer to a NULL pointer, if the server
2740 * sends text or binary we will allocate memory
2741 * for the file and stuff it here
2742 * bytes_to_receive If a file is received, we will store its
2744 * proto_response The protocol response. Caller must provide
2745 * this buffer and ensure that it is at least
2746 * 128 bytes in length.
2748 * This function returns a number equal to the protocol response number,
2749 * -1 if an internal error occurred, -2 if caller provided bad values,
2750 * or 0 - the protocol response number if bad values were found during
2751 * the protocol exchange.
2752 * It stores the protocol response string (minus the number) in
2753 * protocol_response as described above. Some commands send additional
2754 * data in this string.
2756 int CtdlIPCGenericCommand(CtdlIPC * ipc,
2757 const char *command, const char *to_send,
2758 size_t bytes_to_send, char **to_receive, size_t * bytes_to_receive, char *proto_response)
2765 if (!proto_response)
2769 CtdlIPC_putline(ipc, command);
2771 CtdlIPC_getline(ipc, proto_response);
2772 if (proto_response[3] == '*')
2774 ret = atoi(proto_response);
2775 strcpy(proto_response, &proto_response[4]);
2776 switch (ret / 100) {
2777 default: /* Unknown, punt */
2779 case 3: /* MORE_DATA */
2781 /* Don't need to do anything */
2783 case 1: /* LISTING_FOLLOWS */
2784 if (to_receive && !*to_receive && bytes_to_receive) {
2785 *to_receive = CtdlIPCReadListing(ipc, NULL);
2786 } else { /* Drain */
2787 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2791 case 4: /* SEND_LISTING */
2793 CtdlIPCSendListing(ipc, to_send);
2795 /* No listing given, fake it */
2796 CtdlIPC_putline(ipc, "000");
2800 case 6: /* BINARY_FOLLOWS */
2801 if (to_receive && !*to_receive && bytes_to_receive) {
2802 *bytes_to_receive = extract_long(proto_response, 0);
2803 *to_receive = (char *)
2804 malloc((size_t) * bytes_to_receive);
2808 serv_read(ipc, *to_receive, *bytes_to_receive);
2814 drain = extract_long(proto_response, 0);
2815 while (drain > SIZ) {
2816 serv_read(ipc, buf, SIZ);
2819 serv_read(ipc, buf, drain);
2823 case 7: /* SEND_BINARY */
2824 if (to_send && bytes_to_send) {
2825 serv_write(ipc, to_send, bytes_to_send);
2826 } else if (bytes_to_send) {
2827 /* Fake it, send nulls */
2830 fake = bytes_to_send;
2831 memset(buf, '\0', SIZ);
2832 while (fake > SIZ) {
2833 serv_write(ipc, buf, SIZ);
2836 serv_write(ipc, buf, fake);
2838 } /* else who knows? DANGER WILL ROBINSON */
2840 case 8: /* START_CHAT_MODE */
2841 if (!strncasecmp(command, "CHAT", 4)) {
2842 /* Don't call chatmode with generic! */
2843 CtdlIPC_putline(ipc, "/quit");
2846 /* In this mode we send then receive listing */
2848 CtdlIPCSendListing(ipc, to_send);
2850 /* No listing given, fake it */
2851 CtdlIPC_putline(ipc, "000");
2854 if (to_receive && !*to_receive && bytes_to_receive) {
2855 *to_receive = CtdlIPCReadListing(ipc, NULL);
2856 } else { /* Drain */
2857 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2862 case 9: /* ASYNC_MSG */
2863 /* CtdlIPCDoAsync(ret, proto_response); */
2864 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2870 CtdlIPC_unlock(ipc);
2876 * Connect to a Citadel on a remote host using a TCP/IP socket
2878 static int tcp_connectsock(char *host, char *service)
2880 struct in6_addr serveraddr;
2881 struct addrinfo hints;
2882 struct addrinfo *res = NULL;
2883 struct addrinfo *ai = NULL;
2887 if ((host == NULL) || IsEmptyStr(host)) {
2888 service = DEFAULT_HOST;
2890 if ((service == NULL) || IsEmptyStr(service)) {
2891 service = DEFAULT_PORT;
2894 memset(&hints, 0x00, sizeof(hints));
2895 hints.ai_flags = AI_NUMERICSERV;
2896 hints.ai_family = AF_UNSPEC;
2897 hints.ai_socktype = SOCK_STREAM;
2900 * Handle numeric IPv4 and IPv6 addresses
2902 rc = inet_pton(AF_INET, host, &serveraddr);
2903 if (rc == 1) { /* dotted quad */
2904 hints.ai_family = AF_INET;
2905 hints.ai_flags |= AI_NUMERICHOST;
2907 rc = inet_pton(AF_INET6, host, &serveraddr);
2908 if (rc == 1) { /* IPv6 address */
2909 hints.ai_family = AF_INET6;
2910 hints.ai_flags |= AI_NUMERICHOST;
2914 /* Begin the connection process */
2916 rc = getaddrinfo(host, service, &hints, &res);
2922 * Try all available addresses until we connect to one or until we run out.
2924 for (ai = res; ai != NULL; ai = ai->ai_next) {
2925 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2929 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2931 return (sock); /* Connected! */
2933 close(sock); /* Failed. Close the socket to avoid fd leak! */
2945 * Connect to a Citadel on the local host using a unix domain socket
2947 static int uds_connectsock(int *isLocal, char *sockpath)
2949 struct sockaddr_un addr;
2952 memset(&addr, 0, sizeof(addr));
2953 addr.sun_family = AF_UNIX;
2954 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2956 s = socket(AF_UNIX, SOCK_STREAM, 0);
2961 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2972 * input binary data from socket
2974 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes)
2976 unsigned int len, rlen;
2978 #if defined(HAVE_OPENSSL)
2980 serv_read_ssl(ipc, buf, bytes);
2985 while (len < bytes) {
2986 rlen = read(ipc->sock, &buf[len], bytes - len);
2988 connection_died(ipc, 0);
2997 * send binary to server
2999 void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
3001 unsigned int bytes_written = 0;
3004 #if defined(HAVE_OPENSSL)
3006 serv_write_ssl(ipc, buf, nbytes);
3010 while (bytes_written < nbytes) {
3011 retval = write(ipc->sock, &buf[bytes_written], nbytes - bytes_written);
3013 connection_died(ipc, 0);
3016 bytes_written += retval;
3023 * input binary data from encrypted connection
3025 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes)
3031 while (len < bytes) {
3032 if (SSL_want_read(ipc->ssl)) {
3033 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
3034 error_printf("SSL_write in serv_read:\n");
3035 ERR_print_errors_fp(stderr);
3038 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
3042 errval = SSL_get_error(ipc->ssl, rlen);
3043 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
3048 Not sure why we'd want to handle these error codes any differently,
3049 but this definitely isn't the way to handle them. Someone must have
3050 naively assumed that we could fall back to unencrypted communications,
3051 but all it does is just recursively blow the stack.
3052 if (errval == SSL_ERROR_ZERO_RETURN ||
3053 errval == SSL_ERROR_SSL) {
3054 serv_read(ipc, &buf[len], bytes - len);
3058 error_printf("SSL_read in serv_read: %s\n", ERR_reason_error_string(ERR_peek_error()));
3059 connection_died(ipc, 1);
3068 * send binary to server encrypted
3070 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes)
3072 unsigned int bytes_written = 0;
3076 while (bytes_written < nbytes) {
3077 if (SSL_want_write(ipc->ssl)) {
3078 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
3079 error_printf("SSL_read in serv_write:\n");
3080 ERR_print_errors_fp(stderr);
3083 retval = SSL_write(ipc->ssl, &buf[bytes_written], nbytes - bytes_written);
3087 errval = SSL_get_error(ipc->ssl, retval);
3088 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
3092 if (errval == SSL_ERROR_ZERO_RETURN || errval == SSL_ERROR_SSL) {
3093 serv_write(ipc, &buf[bytes_written], nbytes - bytes_written);
3096 error_printf("SSL_write in serv_write: %s\n", ERR_reason_error_string(ERR_peek_error()));
3097 connection_died(ipc, 1);
3100 bytes_written += retval;
3107 static void CtdlIPC_init_OpenSSL(void)
3110 const SSL_METHOD *ssl_method;
3113 /* already done init */
3122 SSL_load_error_strings();
3123 SSLeay_add_ssl_algorithms();
3125 /* Set up the SSL context in which we will oeprate */
3126 ssl_method = SSLv23_client_method();
3127 ssl_ctx = SSL_CTX_new(ssl_method);
3129 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
3132 /* Any reasonable cipher we can get */
3133 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
3134 error_printf("No ciphers available for encryption\n");
3137 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
3139 /* Load DH parameters into the context */
3142 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
3146 if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
3147 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
3152 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
3156 #endif /* HAVE_OPENSSL */
3159 int ReadNetworkChunk(CtdlIPC * ipc)
3174 FD_SET(ipc->sock, &read_fd);
3175 ret = select(ipc->sock + 1, &read_fd, NULL, NULL, &tv);
3179 *(ipc->BufPtr) = '\0';
3180 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3182 ipc->BufPtr[n] = '\0';
3187 } else if (ret < 0) {
3188 if (!(errno == EINTR || errno == EAGAIN))
3189 error_printf("\nselect failed: %d %s\n", err, strerror(err));
3195 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
3197 ipc->BufPtr[n]='\0';
3202 connection_died(ipc, 0);
3210 * input string from socket - implemented in terms of serv_read()
3214 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3217 char *aptr, *bptr, *aeptr, *beptr;
3219 // error_printf("---\n");
3222 #if defined(HAVE_OPENSSL)
3225 /* Read one character at a time. */
3227 serv_read(ipc, &buf[i], 1);
3228 if (buf[i] == '\n' || i == (SIZ - 1))
3232 /* If we got a long line, discard characters until the newline. */
3234 while (buf[i] != '\n')
3235 serv_read(ipc, &buf[i], 1);
3237 /* Strip the trailing newline (and carriage return, if present) */
3238 if (i >= 0 && buf[i] == 10)
3240 if (i >= 0 && buf[i] == 13)
3245 if (ipc->Buf == NULL) {
3247 ipc->Buf = (char *) malloc(ipc->BufSize + 10);
3249 ipc->BufPtr = ipc->Buf;
3253 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3254 if (ipc->BufUsed == 0)
3255 ReadNetworkChunk(ipc);
3257 //// if (ipc->BufUsed != 0) while (1)
3262 aeptr = ipc->Buf + ipc->BufSize;
3263 while ((aptr < aeptr) && (bptr < beptr) && (*aptr != '\0') && (*aptr != '\n'))
3264 *(bptr++) = *(aptr++);
3265 if ((*aptr == '\n') && (aptr < aeptr)) {
3266 /* Terminate it right, remove the line breaks */
3267 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3269 while ((aptr < aeptr) && (*(aptr + 1) == '\0'))
3272 // 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);
3273 if ((bptr > buf + 1) && (*(bptr - 1) == '\r'))
3276 /* is there more in the buffer we need to read later? */
3277 if (ipc->Buf + ipc->BufUsed > aptr) {
3281 ipc->BufPtr = ipc->Buf;
3283 // error_printf("----bla6\n");
3286 } /* should we move our read stuf to the bufferstart so we have more space at the end? */
3287 else if ((ipc->BufPtr != ipc->Buf) && (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4)))) {
3288 size_t NewBufSize = ipc->BufSize * 2;
3289 int delta = (ipc->BufPtr - ipc->Buf);
3292 /* if the line would end after our buffer, we should use a bigger buffer. */
3293 NewBuf = (char *) malloc(NewBufSize + 10);
3294 memcpy(NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3296 ipc->Buf = ipc->BufPtr = NewBuf;
3297 ipc->BufUsed -= delta;
3298 ipc->BufSize = NewBufSize;
3300 if (ReadNetworkChunk(ipc) < 0) {
3301 // error_printf("----bla\n");
3305 /// error_printf("----bl45761%s\nipc->BufUsed");
3307 // error_printf("----bla1\n");
3310 #else /* CHUNKED_READ */
3312 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf)
3316 /* Read one character at a time. */
3318 serv_read(ipc, &buf[i], 1);
3319 if (buf[i] == '\n' || i == (SIZ - 1))
3323 /* If we got a long line, discard characters until the newline. */
3325 while (buf[i] != '\n')
3326 serv_read(ipc, &buf[i], 1);
3328 /* Strip the trailing newline (and carriage return, if present) */
3329 if (i >= 0 && buf[i] == 10)
3331 if (i >= 0 && buf[i] == 13)
3336 #endif /* CHUNKED_READ */
3339 void CtdlIPC_chat_recv(CtdlIPC * ipc, char *buf)
3341 CtdlIPC_getline(ipc, buf);
3345 * send line to server - implemented in terms of serv_write()
3347 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf)
3353 cmd = malloc(len + 2);
3355 /* This requires no extra memory */
3356 serv_write(ipc, buf, len);
3357 serv_write(ipc, "\n", 1);
3359 /* This is network-optimized */
3360 strncpy(cmd, buf, len);
3361 strcpy(cmd + len, "\n");
3362 serv_write(ipc, cmd, len + 1);
3366 ipc->last_command_sent = time(NULL);
3369 void CtdlIPC_chat_send(CtdlIPC * ipc, const char *buf)
3371 CtdlIPC_putline(ipc, buf);
3378 CtdlIPC *CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3386 ipc = malloc(sizeof(struct _CtdlIPC));
3390 #if defined(HAVE_OPENSSL)
3392 CtdlIPC_init_OpenSSL();
3394 ipc->sock = -1; /* Not connected */
3395 ipc->isLocal = 0; /* Not local, of course! */
3396 ipc->downloading = 0;
3398 ipc->last_command_sent = 0L;
3399 ipc->network_status_cb = NULL;
3404 strcpy(cithost, DEFAULT_HOST); /* default host */
3405 strcpy(citport, DEFAULT_PORT); /* default port */
3407 /* Allow caller to supply our values */
3408 if (hostbuf && strlen(hostbuf) > 0) {
3409 strcpy(cithost, hostbuf);
3411 if (portbuf && strlen(portbuf) > 0) {
3412 strcpy(citport, portbuf);
3415 /* Read host/port from command line if present */
3416 for (a = 0; a < argc; ++a) {
3419 } else if (a == 1) {
3420 strcpy(cithost, argv[a]);
3421 } else if (a == 2) {
3422 strcpy(citport, argv[a]);
3424 error_printf("%s: usage: ", argv[0]);
3425 error_printf("%s [host] [port] ", argv[0]);
3432 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3436 /* If we're using a unix domain socket we can do a bunch of stuff */
3437 if (!strcmp(cithost, UDS)) {
3438 if (!strcasecmp(citport, DEFAULT_PORT)) {
3439 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3441 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3443 printf("[%s]\n", sockpath);
3444 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3445 if (ipc->sock == -1) {
3449 if (hostbuf != NULL)
3450 strcpy(hostbuf, cithost);
3451 if (portbuf != NULL)
3452 strcpy(portbuf, sockpath);
3453 strcpy(ipc->ip_hostname, "");
3454 strcpy(ipc->ip_address, "");
3458 printf("[%s:%s]\n", cithost, citport);
3459 ipc->sock = tcp_connectsock(cithost, citport);
3460 if (ipc->sock == -1) {
3466 /* Learn the actual network identity of the host to which we are connected */
3468 struct sockaddr_in6 clientaddr;
3469 unsigned int addrlen = sizeof(clientaddr);
3471 ipc->ip_hostname[0] = 0;
3472 ipc->ip_address[0] = 0;
3474 getpeername(ipc->sock, (struct sockaddr *) &clientaddr, &addrlen);
3475 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0);
3476 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST);
3478 /* stuff other things elsewhere */
3480 if (hostbuf != NULL)
3481 strcpy(hostbuf, cithost);
3482 if (portbuf != NULL)
3483 strcpy(portbuf, citport);
3489 * Disconnect and delete the IPC class (destructor)
3491 void CtdlIPC_delete(CtdlIPC * ipc)
3495 SSL_shutdown(ipc->ssl);
3500 if (ipc->sock > -1) {
3501 shutdown(ipc->sock, 2); /* Close it up */
3504 if (ipc->Buf != NULL)
3513 * Disconnect and delete the IPC class (destructor)
3514 * Also NULLs out the pointer
3516 void CtdlIPC_delete_ptr(CtdlIPC ** pipc)
3518 CtdlIPC_delete(*pipc);
3524 * return the file descriptor of the server socket so we can select() on it.
3526 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3529 int CtdlIPC_getsockfd(CtdlIPC * ipc)
3536 * return one character
3538 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3541 char CtdlIPC_get(CtdlIPC * ipc)
3546 serv_read(ipc, buf, 1);