1 // Copyright (c) 1987-2022 by the citadel.org team
3 // This program is open source software. Use, duplication, and/or
4 // disclosure are subject to the GNU General Purpose License version 3.
6 // This program is distributed in the hope that it will be useful,
7 // but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // GNU General Public License for more details.
11 #include "textclient.h"
14 static SSL_CTX *ssl_ctx;
17 #endif /* HAVE_OPENSSL */
20 #define INADDR_NONE 0xffffffff
23 static void (*status_hook) (char *s) = NULL;
24 char ctdl_autoetc_dir[PATH_MAX] = "";
25 char file_citadel_rc[PATH_MAX] = "";
26 char ctdl_run_dir[PATH_MAX] = "";
27 char ctdl_etc_dir[PATH_MAX] = "";
28 char ctdl_home_directory[PATH_MAX] = "";
29 char file_citadel_socket[PATH_MAX] = "";
51 void CtdlIPC_lock(CtdlIPC * ipc) {
52 if (ipc->network_status_cb)
53 ipc->network_status_cb(1);
57 void CtdlIPC_unlock(CtdlIPC * ipc) {
58 if (ipc->network_status_cb)
59 ipc->network_status_cb(0);
63 char *libcitadelclient_version_string(void) {
64 return "libcitadelclient(unnumbered)";
68 #define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
69 snprintf(SUBDIR,sizeof SUBDIR, "%s%s%s%s%s%s%s", \
70 (home&!relh)?ctdl_home_directory:basedir, \
71 ((basedir!=ctdldir)&(home&!relh))?basedir:"/", \
72 ((basedir!=ctdldir)&(home&!relh))?"/":"", \
74 (relhome[0]!='\0')?"/":"",\
76 (dirbuffer[0]!='\0')?"/":"");
78 #define DBG_PRINT(A) if (dbg==1) fprintf (stderr,"%s : %s \n", #A, A)
81 void calc_dirs_n_files(int relh, int home, const char *relhome, char *ctdldir, int dbg) {
82 const char *basedir = "";
83 char dirbuffer[PATH_MAX] = "";
85 StripSlashes(ctdldir, 1);
92 COMPUTE_DIRECTORY(ctdl_run_dir);
93 StripSlashes(ctdl_run_dir, 1);
96 #ifndef HAVE_AUTO_ETC_DIR
99 basedir = AUTO_ETC_DIR;
101 COMPUTE_DIRECTORY(ctdl_autoetc_dir);
102 StripSlashes(ctdl_autoetc_dir, 1);
110 COMPUTE_DIRECTORY(ctdl_etc_dir);
111 StripSlashes(ctdl_etc_dir, 1);
115 snprintf(file_citadel_rc, sizeof file_citadel_rc, "%scitadel.rc", ctdl_etc_dir);
116 StripSlashes(file_citadel_rc, 0);
118 snprintf(file_citadel_socket, sizeof file_citadel_socket, "%scitadel.socket", ctdl_run_dir);
119 StripSlashes(file_citadel_socket, 0);
121 DBG_PRINT(ctdl_run_dir);
122 DBG_PRINT(file_citadel_socket);
123 DBG_PRINT(ctdl_etc_dir);
124 DBG_PRINT(file_citadel_rc);
127 void setCryptoStatusHook(void (*hook) (char *s)) {
131 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC * ipc, void (*hook) (int state)) {
132 ipc->network_status_cb = hook;
136 char instant_msgs = 0;
139 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes);
140 static void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes);
142 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes);
143 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes);
144 static void endtls(SSL * ssl);
145 #endif /* HAVE_OPENSSL */
146 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf);
147 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf);
151 const char *svn_revision(void);
154 * Does nothing. The server should always return 200.
156 int CtdlIPCNoop(CtdlIPC * ipc) {
159 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
164 * Does nothing interesting. The server should always return 200
165 * along with your string.
167 int CtdlIPCEcho(CtdlIPC * ipc, const char *arg, char *cret) {
176 aaa = (char *) malloc((size_t) (strlen(arg) + 6));
180 sprintf(aaa, "ECHO %s", arg);
181 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
188 * Asks the server to close the connecction.
189 * Should always return 200.
191 int CtdlIPCQuit(CtdlIPC * ipc) {
192 int ret = 221; /* Default to successful quit */
196 if (ipc->sock > -1) {
197 CtdlIPC_putline(ipc, "QUIT");
198 CtdlIPC_getline(ipc, aaa);
203 SSL_shutdown(ipc->ssl);
207 shutdown(ipc->sock, 2); /* Close connection; we're dead */
215 * Asks the server to log out. Should always return 200, even if no user
216 * was logged in. The user will not be logged in after this!
218 int CtdlIPCLogout(CtdlIPC * ipc) {
223 CtdlIPC_putline(ipc, "LOUT");
224 CtdlIPC_getline(ipc, aaa);
232 * First stage of authentication - pass the username. Returns 300 if the
233 * username is able to log in, with the username correctly spelled in cret.
234 * Returns various 500 error codes if the user doesn't exist, etc.
236 int CtdlIPCTryLogin(CtdlIPC * ipc, const char *username, char *cret) {
245 aaa = (char *) malloc((size_t) (strlen(username) + 6));
249 sprintf(aaa, "USER %s", username);
250 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
257 * Second stage of authentication - provide password. The server returns
258 * 200 and several arguments in cret relating to the user's account.
260 int CtdlIPCTryPassword(CtdlIPC * ipc, const char *passwd, char *cret) {
269 aaa = (char *) malloc((size_t) (strlen(passwd) + 6));
273 sprintf(aaa, "PASS %s", passwd);
274 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
281 * Create a new user. This returns 200 plus the same arguments as TryPassword
282 * if selfservice is nonzero, unless there was a problem creating the account.
283 * If selfservice is zero, creates a new user but does not log out the existing
284 * user - intended for use by system administrators to create accounts on
285 * behalf of other users.
287 int CtdlIPCCreateUser(CtdlIPC * ipc, const char *username, int selfservice, char *cret) {
296 aaa = (char *) malloc((size_t) (strlen(username) + 6));
300 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
301 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
308 * Changes the user's password. Returns 200 if changed, errors otherwise.
310 int CtdlIPCChangePassword(CtdlIPC * ipc, const char *passwd, char *cret) {
319 aaa = (char *) malloc((size_t) (strlen(passwd) + 6));
323 sprintf(aaa, "SETP %s", passwd);
324 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
331 /* Caller must free the march list */
332 /* Room types are defined in enum RoomList; keep these in sync! */
333 /* floor is -1 for all, or floornum */
334 int CtdlIPCKnownRooms(CtdlIPC * ipc, enum RoomList which, int floor, struct march **listing, char *cret) {
336 struct march *march = NULL;
337 static char *proto[] = { "LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
345 return -2; /* Free the listing first */
348 /* if (which < 0 || which > 4) return -2; */
350 return -2; /* Can't validate upper bound, sorry */
352 sprintf(aaa, "%s %d", proto[which], floor);
353 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
354 if (ret / 100 == 1) {
357 while (bbb && strlen(bbb)) {
360 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
362 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
363 mptr = (struct march *) malloc(sizeof(struct march));
366 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
367 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
368 mptr->march_floor = (char) extract_int(aaa, 2);
369 mptr->march_order = (char) extract_int(aaa, 3);
370 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
371 mptr->march_access = (char) extract_int(aaa, 5);
378 while (mptr2->next != NULL)
393 /* Caller must free the struct ctdluser; caller may pass an existing one */
394 int CtdlIPCGetConfig(CtdlIPC * ipc, struct ctdluser **uret, char *cret) {
402 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
406 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
407 if (ret / 100 == 2) {
408 uret[0]->flags = extract_int(cret, 2);
415 int CtdlIPCSetConfig(CtdlIPC * ipc, struct ctdluser *uret, char *cret) {
423 sprintf(aaa, "SETU 80|24|%d", uret->flags);
424 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
429 int CtdlIPCRenameUser(CtdlIPC * ipc, char *oldname, char *newname, char *cret) {
440 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
441 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
447 int CtdlIPCGotoRoom(CtdlIPC * ipc, const char *room, const char *passwd, struct ctdlipcroom **rret, char *cret) {
456 *rret = (struct ctdlipcroom *) calloc(1, sizeof(struct ctdlipcroom));
461 aaa = (char *) malloc(strlen(room) + strlen(passwd) + 7);
466 sprintf(aaa, "GOTO %s|%s", room, passwd);
468 aaa = (char *) malloc(strlen(room) + 6);
473 sprintf(aaa, "GOTO %s", room);
475 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
476 if (ret / 100 == 2) {
477 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
478 rret[0]->RRunread = extract_long(cret, 1);
479 rret[0]->RRtotal = extract_long(cret, 2);
480 rret[0]->RRinfoupdated = extract_int(cret, 3);
481 rret[0]->RRflags = extract_int(cret, 4);
482 rret[0]->RRhighest = extract_long(cret, 5);
483 rret[0]->RRlastread = extract_long(cret, 6);
484 rret[0]->RRismailbox = extract_int(cret, 7);
485 rret[0]->RRaide = extract_int(cret, 8);
486 rret[0]->RRnewmail = extract_long(cret, 9);
487 rret[0]->RRfloor = extract_int(cret, 10);
488 rret[0]->RRcurrentview = extract_int(cret, 11);
489 rret[0]->RRdefaultview = extract_int(cret, 12);
490 /* position 13 is a trash folder flag ... irrelevant in this client */
491 rret[0]->RRflags2 = extract_int(cret, 14);
502 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
503 /* whicharg is number of messages, applies to last, first, gt, lt */
504 int CtdlIPCGetMessages(CtdlIPC * ipc, enum MessageList which, int whicharg, const char *mtemplate, unsigned long **mret, char *cret)
507 unsigned long count = 0;
508 static char *proto[] = { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
519 if (which < 0 || which > 6)
523 sprintf(aaa, "MSGS %s||%d", proto[which], (mtemplate) ? 1 : 0);
525 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg, (mtemplate) ? 1 : 0);
527 count = strlen(mtemplate);
528 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
532 *mret = (unsigned long *) calloc(1, sizeof(unsigned long));
535 while (bbb && strlen(bbb)) {
536 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
537 remove_token(bbb, 0, '\n');
538 *mret = (unsigned long *) realloc(*mret, (size_t) ((count + 2) * sizeof(unsigned long)));
540 (*mret)[count++] = atol(aaa);
553 int CtdlIPCGetSingleMessage(CtdlIPC * ipc, long msgnum, int headers, int as_mime, struct ctdlipcmessage **mret, char *cret)
559 int multipart_hunting = 0;
560 char multipart_prefix[128];
568 *mret = (struct ctdlipcmessage *) calloc(1, sizeof(struct ctdlipcmessage));
574 strcpy(encoding, "");
575 strcpy(mret[0]->content_type, "");
576 mret[0]->is_local = 0;
577 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
578 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
579 if (ret / 100 == 1) {
581 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
582 while (strlen(bbb) > 4 && bbb[4] == '=') {
583 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
584 remove_token(bbb, 0, '\n');
586 if (!strncasecmp(aaa, "nhdr=yes", 8))
588 else if (!strncasecmp(aaa, "from=", 5))
589 strncpy(mret[0]->author, &aaa[5], SIZ);
590 else if (!strncasecmp(aaa, "type=", 5))
591 mret[0]->type = atoi(&aaa[5]);
592 else if (!strncasecmp(aaa, "msgn=", 5))
593 strncpy(mret[0]->msgid, &aaa[5], SIZ);
594 else if (!strncasecmp(aaa, "subj=", 5))
595 strncpy(mret[0]->subject, &aaa[5], SIZ);
596 else if (!strncasecmp(aaa, "rfca=", 5))
597 strncpy(mret[0]->email, &aaa[5], SIZ);
598 else if (!strncasecmp(aaa, "room=", 5))
599 strncpy(mret[0]->room, &aaa[5], SIZ);
600 else if (!strncasecmp(aaa, "rcpt=", 5))
601 strncpy(mret[0]->recipient, &aaa[5], SIZ);
602 else if (!strncasecmp(aaa, "wefw=", 5))
603 strncpy(mret[0]->references, &aaa[5], SIZ);
604 else if (!strncasecmp(aaa, "time=", 5))
605 mret[0]->time = atol(&aaa[5]);
606 else if (!strncasecmp(aaa, "locl", 4))
607 mret[0]->is_local = 1;
609 /* Multipart/alternative prefix & suffix strings help
610 * us to determine which part we want to download.
612 else if (!strncasecmp(aaa, "pref=", 5)) {
613 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
614 if (!strcasecmp(multipart_prefix, "multipart/alternative")) {
617 } else if (!strncasecmp(aaa, "suff=", 5)) {
618 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
619 if (!strcasecmp(multipart_prefix, "multipart/alternative")) {
624 else if (!strncasecmp(aaa, "part=", 5)) {
625 struct parts *ptr, *chain;
627 ptr = (struct parts *) calloc(1, sizeof(struct parts));
630 /* Fill the buffers for the caller */
631 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
632 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
633 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
634 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
635 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
636 ptr->length = extract_long(&aaa[5], 5);
637 if (!mret[0]->attachments)
638 mret[0]->attachments = ptr;
640 chain = mret[0]->attachments;
646 /* Now handle multipart/alternative */
647 if (multipart_hunting > 0) {
648 if ((!strcasecmp(ptr->mimetype, "text/plain"))
649 || (!strcasecmp(ptr->mimetype, "text/html"))) {
650 strcpy(mret[0]->mime_chosen, ptr->number);
657 /* Eliminate "text\n" */
658 remove_token(bbb, 0, '\n');
660 /* If doing a MIME thing, pull out the extra headers */
663 if (!strncasecmp(bbb, "Content-type:", 13)) {
664 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
665 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
666 striplt(mret[0]->content_type);
668 /* strip out ";charset=" portion. FIXME do something with
669 * the charset (like... convert it) instead of just throwing
672 if (strstr(mret[0]->content_type, ";") != NULL) {
673 strcpy(strstr(mret[0]->content_type, ";"), "");
677 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
678 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
679 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
680 striplt(mret[0]->mime_chosen);
682 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
683 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
684 strcpy(encoding, &encoding[26]);
687 remove_token(bbb, 0, '\n');
688 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
689 remove_token(bbb, 0, '\n');
696 if ((!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable"))) {
698 int bytes_decoded = 0;
699 ccc = malloc(strlen(bbb) + 32768);
700 if (!strcasecmp(encoding, "base64")) {
701 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
702 } else if (!strcasecmp(encoding, "quoted-printable")) {
703 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
705 ccc[bytes_decoded] = 0;
710 /* FIXME: Strip trailing whitespace */
711 bbb = (char *) realloc(bbb, (size_t) (strlen(bbb) + 1));
714 bbb = (char *) realloc(bbb, 1);
724 int CtdlIPCWhoKnowsRoom(CtdlIPC * ipc, char **listing, char *cret)
736 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
742 int CtdlIPCServerInfo(CtdlIPC * ipc, char *cret)
746 char *listing = NULL;
752 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
753 if (ret / 100 == 1) {
756 while (*listing && strlen(listing)) {
757 extract_token(buf, listing, 0, '\n', sizeof buf);
758 remove_token(listing, 0, '\n');
761 ipc->ServInfo.pid = atoi(buf);
764 strcpy(ipc->ServInfo.nodename, buf);
767 strcpy(ipc->ServInfo.humannode, buf);
770 strcpy(ipc->ServInfo.fqdn, buf);
773 strcpy(ipc->ServInfo.software, buf);
776 ipc->ServInfo.rev_level = atoi(buf);
779 strcpy(ipc->ServInfo.site_location, buf);
782 strcpy(ipc->ServInfo.sysadm, buf);
785 strcpy(ipc->ServInfo.moreprompt, buf);
788 ipc->ServInfo.ok_floors = atoi(buf);
791 ipc->ServInfo.supports_ldap = atoi(buf);
794 ipc->ServInfo.newuser_disabled = atoi(buf);
797 strcpy(ipc->ServInfo.default_cal_zone, buf);
800 ipc->ServInfo.load_avg = atof(buf);
803 ipc->ServInfo.worker_avg = atof(buf);
806 ipc->ServInfo.thread_count = atoi(buf);
809 ipc->ServInfo.has_sieve = atoi(buf);
812 ipc->ServInfo.fulltext_enabled = atoi(buf);
815 strcpy(ipc->ServInfo.svn_revision, buf);
818 ipc->ServInfo.guest_logins = atoi(buf);
831 int CtdlIPCReadDirectory(CtdlIPC * ipc, char **listing, char *cret)
843 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
849 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
851 int CtdlIPCSetLastRead(CtdlIPC * ipc, long msgnum, char *cret)
860 sprintf(aaa, "SLRP %ld", msgnum);
862 sprintf(aaa, "SLRP HIGHEST");
864 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
870 int CtdlIPCInviteUserToRoom(CtdlIPC * ipc, const char *username, char *cret)
880 aaa = (char *) malloc(strlen(username) + 6);
884 sprintf(aaa, "INVT %s", username);
885 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
892 int CtdlIPCKickoutUserFromRoom(CtdlIPC * ipc, const char *username, char *cret)
902 aaa = (char *) malloc(strlen(username) + 6);
904 sprintf(aaa, "KICK %s", username);
905 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
912 int CtdlIPCGetRoomAttributes(CtdlIPC * ipc, struct ctdlroom **qret, char *cret)
921 *qret = (struct ctdlroom *) calloc(1, sizeof(struct ctdlroom));
925 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
926 if (ret / 100 == 2) {
927 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
928 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
929 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
930 qret[0]->QRflags = extract_int(cret, 3);
931 qret[0]->QRfloor = extract_int(cret, 4);
932 qret[0]->QRorder = extract_int(cret, 5);
933 qret[0]->QRdefaultview = extract_int(cret, 6);
934 qret[0]->QRflags2 = extract_int(cret, 7);
941 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc,
942 int forget, // if nonzero, kick all users out of the room
943 struct ctdlroom *qret,
954 cmd = (char *) malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) + strlen(qret->QRdirname) + 64);
958 sprintf(cmd, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
959 qret->QRname, qret->QRpasswd, qret->QRdirname,
960 qret->QRflags, forget, qret->QRfloor, qret->QRorder, qret->QRdefaultview, qret->QRflags2);
961 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
968 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
973 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
978 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret) {
987 aaa = (char *) malloc(strlen(username) + 6);
991 sprintf(aaa, "SETA %s", username);
992 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
999 int CtdlIPCPostMessage(CtdlIPC * ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
1010 if (mr->references) {
1011 for (ptr = mr->references; *ptr != 0; ++ptr) {
1017 snprintf(cmd, sizeof cmd,
1018 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
1019 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
1020 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL, NULL, cret);
1021 if ((flag == 0) && (subject_required != NULL)) {
1022 /* Is the server strongly recommending that the user enter a message subject? */
1023 if ((cret[3] != '\0') && (cret[4] != '\0')) {
1024 *subject_required = extract_int(&cret[4], 1);
1034 int CtdlIPCRoomInfo(CtdlIPC * ipc, char **iret, char *cret) {
1044 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
1049 int CtdlIPCDeleteMessage(CtdlIPC * ipc, long msgnum, char *cret) {
1057 sprintf(aaa, "DELE %ld", msgnum);
1058 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1063 int CtdlIPCMoveMessage(CtdlIPC * ipc, int copy, long msgnum, const char *destroom, char *cret) {
1074 aaa = (char *) malloc(strlen(destroom) + 28);
1078 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
1079 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1086 int CtdlIPCDeleteRoom(CtdlIPC * ipc, int for_real, char *cret) {
1092 sprintf(aaa, "KILL %d", for_real);
1093 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1098 int CtdlIPCCreateRoom(CtdlIPC * ipc, int for_real, const char *roomname, int type, const char *password, int floor, char *cret)
1109 aaa = (char *) malloc(strlen(roomname) + strlen(password) + 40);
1112 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type, password, floor);
1115 aaa = (char *) malloc(strlen(roomname) + 40);
1118 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type, floor);
1120 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1127 int CtdlIPCForgetRoom(CtdlIPC * ipc, char *cret) {
1131 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1136 int CtdlIPCSystemMessage(CtdlIPC * ipc, const char *message, char **mret, char *cret) {
1150 aaa = (char *) malloc(strlen(message) + 6);
1154 sprintf(aaa, "MESG %s", message);
1155 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1162 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret) {
1166 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1171 int CtdlIPCGetUserRegistration(CtdlIPC * ipc, const char *username, char **rret, char *cret) {
1184 aaa = (char *) malloc(strlen(username) + 6);
1186 aaa = (char *) malloc(12);
1191 sprintf(aaa, "GREG %s", username);
1193 sprintf(aaa, "GREG _SELF_");
1194 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1201 int CtdlIPCValidateUser(CtdlIPC * ipc, const char *username, int axlevel, char *cret) {
1209 if (axlevel < AxDeleted || axlevel > AxAideU)
1212 aaa = (char *) malloc(strlen(username) + 17);
1216 sprintf(aaa, "VALI %s|%d", username, axlevel);
1217 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1224 int CtdlIPCSetRoomInfo(CtdlIPC * ipc, int for_real, const char *info, char *cret)
1233 sprintf(aaa, "EINF %d", for_real);
1234 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1239 int CtdlIPCUserListing(CtdlIPC * ipc, char *searchstring, char **listing, char *cret)
1254 cmd = malloc(strlen(searchstring) + 10);
1255 sprintf(cmd, "LIST %s", searchstring);
1257 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1264 int CtdlIPCSetRegistration(CtdlIPC * ipc, const char *info, char *cret)
1271 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info), NULL, NULL, cret);
1276 int CtdlIPCMiscCheck(CtdlIPC * ipc, struct ctdlipcmisc *chek, char *cret)
1285 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1286 if (ret / 100 == 2) {
1287 chek->newmail = extract_long(cret, 0);
1288 chek->needregis = extract_int(cret, 1);
1289 chek->needvalid = extract_int(cret, 2);
1296 int CtdlIPCDeleteFile(CtdlIPC * ipc, const char *filename, char *cret)
1306 aaa = (char *) malloc(strlen(filename) + 6);
1310 sprintf(aaa, "DELF %s", filename);
1311 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1318 int CtdlIPCMoveFile(CtdlIPC * ipc, const char *filename, const char *destroom, char *cret)
1330 aaa = (char *) malloc(strlen(filename) + strlen(destroom) + 7);
1334 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1335 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1342 int CtdlIPCOnlineUsers(CtdlIPC * ipc, char **listing, time_t * stamp, char *cret)
1354 *stamp = CtdlIPCServerTime(ipc, cret);
1356 *stamp = time(NULL);
1357 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1363 int CtdlIPCFileDownload(CtdlIPC * ipc, const char *filename, void **buf, size_t resume, void (*progress_gauge_callback)
1364 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1380 if (ipc->downloading)
1383 aaa = (char *) malloc(strlen(filename) + 6);
1387 sprintf(aaa, "OPEN %s", filename);
1388 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1390 if (ret / 100 == 2) {
1391 ipc->downloading = 1;
1392 bytes = extract_long(cret, 0);
1393 last_mod = extract_int(cret, 1);
1394 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1395 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume, progress_gauge_callback, cret);
1396 ret = CtdlIPCEndDownload(ipc, cret);
1398 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1405 int CtdlIPCAttachmentDownload(CtdlIPC * ipc, long msgnum, const char *part, void **buf, void (*progress_gauge_callback)
1406 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1425 if (ipc->downloading)
1428 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1429 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1430 if (ret / 100 == 2) {
1431 ipc->downloading = 1;
1432 bytes = extract_long(cret, 0);
1433 last_mod = extract_int(cret, 1);
1434 extract_token(filename, cret, 2, '|', sizeof filename);
1435 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1436 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1437 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1438 ret = CtdlIPCEndDownload(ipc, cret);
1440 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1447 int CtdlIPCImageDownload(CtdlIPC * ipc, const char *filename, void **buf, void (*progress_gauge_callback)
1448 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1464 if (ipc->downloading)
1467 aaa = (char *) malloc(strlen(filename) + 6);
1471 sprintf(aaa, "OIMG %s", filename);
1472 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1474 if (ret / 100 == 2) {
1475 ipc->downloading = 1;
1476 bytes = extract_long(cret, 0);
1477 last_mod = extract_int(cret, 1);
1478 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1479 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1480 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1481 ret = CtdlIPCEndDownload(ipc, cret);
1483 sprintf(cret, "%d|%ld|%s|%s", (int) bytes, last_mod, filename, mimetype);
1490 int CtdlIPCFileUpload(CtdlIPC * ipc, const char *save_as, const char *comment, const char *path, void (*progress_gauge_callback)
1491 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1496 char MimeTestBuf[64];
1497 const char *MimeType;
1513 uploadFP = fopen(path, "r");
1517 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1522 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1523 aaa = (char *) malloc(strlen(save_as) + strlen(MimeType) + strlen(comment) + 7);
1527 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1528 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1530 if (ret / 100 == 2) {
1532 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1533 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1541 int CtdlIPCImageUpload(CtdlIPC * ipc, int for_real, const char *path, const char *save_as, void (*progress_gauge_callback)
1542 (CtdlIPC *, unsigned long, unsigned long), char *cret)
1547 char MimeTestBuf[64];
1548 const char *MimeType;
1555 if (!path && for_real)
1557 if (!*path && for_real)
1562 aaa = (char *) malloc(strlen(save_as) + 17);
1566 uploadFP = fopen(path, "r");
1570 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1574 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1576 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1577 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1579 if (ret / 100 == 2 && for_real) {
1581 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1582 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1590 int CtdlIPCQueryUsername(CtdlIPC * ipc, const char *username, char *cret)
1600 aaa = (char *) malloc(strlen(username) + 6);
1604 sprintf(aaa, "QUSR %s", username);
1605 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1612 int CtdlIPCFloorListing(CtdlIPC * ipc, char **listing, char *cret)
1623 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1628 int CtdlIPCCreateFloor(CtdlIPC * ipc, int for_real, const char *name, char *cret)
1638 sprintf(aaa, "CFLR %s|%d", name, for_real);
1639 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1645 int CtdlIPCDeleteFloor(CtdlIPC * ipc, int for_real, int floornum, char *cret)
1654 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1655 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1660 int CtdlIPCEditFloor(CtdlIPC * ipc, int floornum, const char *floorname, char *cret)
1672 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1673 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1681 * You only need to fill out hostname, the defaults will be used if any of the
1682 * other fields are not set properly.
1684 int CtdlIPCIdentifySoftware(CtdlIPC * ipc, int developerid, int clientid,
1685 int revision, const char *software_name, const char *hostname, char *cret)
1690 if (developerid < 0 || clientid < 0 || revision < 0 || !software_name) {
1693 revision = CLIENT_VERSION - 600;
1694 software_name = "Citadel (libcitadel)";
1699 aaa = (char *) malloc(strlen(software_name) + strlen(hostname) + 29);
1703 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid, revision, software_name, hostname);
1704 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1711 int CtdlIPCSendInstantMessage(CtdlIPC * ipc, const char *username, const char *text, char *cret)
1721 aaa = (char *) malloc(strlen(username) + 8);
1726 sprintf(aaa, "SEXP %s|-", username);
1727 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1729 sprintf(aaa, "SEXP %s||", username);
1730 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1738 int CtdlIPCGetInstantMessage(CtdlIPC * ipc, char **listing, char *cret)
1749 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1754 /* mode is 0 = enable, 1 = disable, 2 = status */
1755 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC * ipc, int mode, char *cret)
1762 sprintf(aaa, "DEXP %d", mode);
1763 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1768 int CtdlIPCSetBio(CtdlIPC * ipc, char *bio, char *cret)
1775 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio), NULL, NULL, cret);
1780 int CtdlIPCGetBio(CtdlIPC * ipc, const char *username, char **listing, char *cret)
1795 aaa = (char *) malloc(strlen(username) + 6);
1799 sprintf(aaa, "RBIO %s", username);
1800 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1807 int CtdlIPCListUsersWithBios(CtdlIPC * ipc, char **listing, char *cret)
1818 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1823 int CtdlIPCStealthMode(CtdlIPC * ipc, int mode, char *cret)
1830 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1831 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1836 int CtdlIPCTerminateSession(CtdlIPC * ipc, int sid, char *cret)
1843 sprintf(aaa, "TERM %d", sid);
1844 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1849 int CtdlIPCTerminateServerNow(CtdlIPC * ipc, char *cret)
1854 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1859 int CtdlIPCTerminateServerScheduled(CtdlIPC * ipc, int mode, char *cret)
1866 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1867 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1872 int CtdlIPCEnterSystemMessage(CtdlIPC * ipc, const char *filename, const char *text, char *cret)
1884 aaa = (char *) malloc(strlen(filename) + 6);
1888 sprintf(aaa, "EMSG %s", filename);
1889 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1896 /* This function returns the actual server time reported, or 0 if error */
1897 time_t CtdlIPCServerTime(CtdlIPC * ipc, char *cret)
1902 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1903 if (ret / 100 == 2) {
1904 tret = extract_long(cret, 0);
1913 int CtdlIPCAideGetUserParameters(CtdlIPC * ipc, const char *who, struct ctdluser **uret, char *cret)
1923 *uret = (struct ctdluser *) calloc(1, sizeof(struct ctdluser));
1927 sprintf(aaa, "AGUP %s", who);
1928 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1930 if (ret / 100 == 2) {
1931 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1932 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1933 uret[0]->flags = extract_int(cret, 2);
1934 uret[0]->timescalled = extract_long(cret, 3);
1935 uret[0]->posted = extract_long(cret, 4);
1936 uret[0]->axlevel = extract_int(cret, 5);
1937 uret[0]->usernum = extract_long(cret, 6);
1938 uret[0]->lastcall = extract_long(cret, 7);
1939 uret[0]->USuserpurge = extract_int(cret, 8);
1946 int CtdlIPCAideSetUserParameters(CtdlIPC * ipc, const struct ctdluser *uret, char *cret)
1956 aaa = (char *) malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1960 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1961 uret->fullname, uret->password, uret->flags, uret->timescalled,
1962 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge);
1963 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1970 int CtdlIPCAideGetEmailAddresses(CtdlIPC * ipc, const char *who, char *target_buf, char *cret)
1974 char *emailaddrs = NULL;
1975 size_t emailaddrs_len = 0;
1977 sprintf(aaa, "AGEA %s", who);
1978 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
1980 if (ret / 100 == 1) {
1981 strcpy(target_buf, emailaddrs);
1984 if (emailaddrs != NULL) {
1993 int CtdlIPCAideSetEmailAddresses(CtdlIPC * ipc, const char *who, char *emailaddrs, char *cret)
2005 sprintf(aaa, "ASEA %s", who);
2006 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
2012 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2013 /* caller must free the struct ExpirePolicy */
2014 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC * ipc, GPEXWhichPolicy which, struct ExpirePolicy **policy, char *cret)
2016 static char *proto[] = {
2020 strof(mailboxespolicy)
2030 *policy = (struct ExpirePolicy *) calloc(1, sizeof(struct ExpirePolicy));
2033 if (which < 0 || which > 3)
2036 sprintf(cmd, "GPEX %s", proto[which]);
2037 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2038 if (ret / 100 == 2) {
2039 policy[0]->expire_mode = extract_int(cret, 0);
2040 policy[0]->expire_value = extract_int(cret, 1);
2047 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
2048 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
2049 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC * ipc, int which, struct ExpirePolicy *policy, char *cret)
2052 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2056 if (which < 0 || which > 3)
2060 if (policy->expire_mode < 0 || policy->expire_mode > 3)
2062 if (policy->expire_mode >= 2 && policy->expire_value < 1)
2065 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which], policy->expire_mode, policy->expire_value);
2066 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2071 int CtdlIPCGetSystemConfig(CtdlIPC * ipc, char **listing, char *cret)
2082 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0, listing, &bytes, cret);
2087 int CtdlIPCSetSystemConfig(CtdlIPC * ipc, const char *listing, char *cret)
2094 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing), NULL, NULL, cret);
2099 int CtdlIPCGetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, char **listing, char *cret)
2114 aaa = malloc(strlen(mimetype) + 13);
2117 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2118 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2125 int CtdlIPCSetSystemConfigByType(CtdlIPC * ipc, const char *mimetype, const char *listing, char *cret)
2137 aaa = malloc(strlen(mimetype) + 13);
2140 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2141 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2148 int CtdlIPCGetRoomNetworkConfig(CtdlIPC * ipc, char **listing, char *cret)
2159 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2164 int CtdlIPCSetRoomNetworkConfig(CtdlIPC * ipc, const char *listing, char *cret)
2171 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2176 int CtdlIPCRequestClientLogout(CtdlIPC * ipc, int session, char *cret)
2185 sprintf(aaa, "REQT %d", session);
2186 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2191 int CtdlIPCSetMessageSeen(CtdlIPC * ipc, long msgnum, int seen, char *cret)
2200 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2201 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2206 int CtdlIPCStartEncryption(CtdlIPC * ipc, char *cret)
2215 /* New SSL object */
2216 temp_ssl = SSL_new(ssl_ctx);
2218 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2221 /* Pointless flag waving */
2222 #if SSLEAY_VERSION_NUMBER >= 0x0922
2223 SSL_set_session_id_context(temp_ssl, (const unsigned char *) "Citadel SID", 14);
2226 /* Associate network connection with SSL object */
2227 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2228 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2232 if (status_hook != NULL) {
2233 status_hook("Requesting encryption...\r");
2236 /* Ready to start SSL/TLS */
2237 r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2239 error_printf("Server can't start TLS: %s\n", buf);
2244 /* Do SSL/TLS handshake */
2245 if ((a = SSL_connect(temp_ssl)) < 1) {
2246 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2250 ipc->ssl = temp_ssl;
2252 error_printf("Encrypting with %s cipher %s\n",
2253 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)), SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2258 #endif /* HAVE_OPENSSL */
2263 static void endtls(SSL * ssl)
2274 int CtdlIPCDirectoryLookup(CtdlIPC * ipc, const char *address, char *cret)
2284 aaa = (char *) malloc(strlen(address) + 6);
2288 sprintf(aaa, "QDIR %s", address);
2289 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2296 int CtdlIPCInternalProgram(CtdlIPC * ipc, int secret, char *cret)
2302 sprintf(aaa, "IPGM %d", secret);
2303 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2309 /* ************************************************************************** */
2310 /* Stuff below this line is not for public consumption */
2311 /* ************************************************************************** */
2314 /* Read a listing from the server up to 000. Append to dest if it exists */
2315 char *CtdlIPCReadListing(CtdlIPC * ipc, char *dest)
2324 length = strlen(ret);
2329 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2330 linelength = strlen(aaa);
2331 ret = (char *) realloc(ret, (size_t) (length + linelength + 2));
2333 strcpy(&ret[length], aaa);
2334 length += linelength;
2335 strcpy(&ret[length++], "\n");
2343 /* Send a listing to the server; generate the ending 000. */
2344 int CtdlIPCSendListing(CtdlIPC * ipc, const char *listing)
2348 text = (char *) malloc(strlen(listing) + 6);
2350 strcpy(text, listing);
2351 while (text[strlen(text) - 1] == '\n')
2352 text[strlen(text) - 1] = '\0';
2353 strcat(text, "\n000");
2354 CtdlIPC_putline(ipc, text);
2358 /* Malloc failed but we are committed to send */
2359 /* This may result in extra blanks at the bottom */
2360 CtdlIPC_putline(ipc, text);
2361 CtdlIPC_putline(ipc, "000");
2367 /* Partial read of file from server */
2368 size_t CtdlIPCPartialRead(CtdlIPC * ipc, void **buf, size_t offset, size_t bytes, char *cret)
2381 sprintf(aaa, "READ %d|%d", (int) offset, (int) bytes);
2382 CtdlIPC_putline(ipc, aaa);
2383 CtdlIPC_getline(ipc, aaa);
2385 strcpy(cret, &aaa[4]);
2387 len = extract_long(&aaa[4], 0);
2388 *buf = (void *) realloc(*buf, (size_t) (offset + len));
2390 /* I know what I'm doing */
2391 serv_read(ipc, ((char *) (*buf) + offset), len);
2393 /* We have to read regardless */
2394 serv_read(ipc, aaa, len);
2398 CtdlIPC_unlock(ipc);
2404 int CtdlIPCEndDownload(CtdlIPC * ipc, char *cret)
2410 if (!ipc->downloading)
2413 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2415 ipc->downloading = 0;
2421 int CtdlIPCSpecifyPreferredFormats(CtdlIPC * ipc, char *cret, char *formats)
2426 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2427 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2434 int CtdlIPCReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2435 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2445 if (!ipc->downloading)
2449 if (progress_gauge_callback)
2450 progress_gauge_callback(ipc, len, bytes);
2451 while (len < bytes) {
2454 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2460 if (progress_gauge_callback)
2461 progress_gauge_callback(ipc, len, bytes);
2466 /* READ - pipelined */
2467 int CtdlIPCHighSpeedReadDownload(CtdlIPC * ipc, void **buf, size_t bytes, size_t resume, void (*progress_gauge_callback)
2468 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2471 int calls; /* How many calls in the pipeline */
2472 int i; /* iterator */
2481 if (!ipc->downloading)
2484 *buf = (void *) realloc(*buf, bytes - resume);
2490 if (progress_gauge_callback)
2491 progress_gauge_callback(ipc, len, bytes);
2493 /* How many calls will be in the pipeline? */
2494 calls = (bytes - resume) / 4096;
2495 if ((bytes - resume) % 4096)
2498 /* Send all requests at once */
2499 for (i = 0; i < calls; i++) {
2500 sprintf(aaa, "READ %d|4096", (int) (i * 4096 + resume));
2501 CtdlIPC_putline(ipc, aaa);
2504 /* Receive all responses at once */
2505 for (i = 0; i < calls; i++) {
2506 CtdlIPC_getline(ipc, aaa);
2508 strcpy(cret, &aaa[4]);
2510 len = extract_long(&aaa[4], 0);
2511 /* I know what I'm doing */
2512 serv_read(ipc, ((char *) (*buf) + (i * 4096)), len);
2514 if (progress_gauge_callback)
2515 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2517 CtdlIPC_unlock(ipc);
2523 int CtdlIPCEndUpload(CtdlIPC * ipc, int discard, char *cret)
2530 if (!ipc->uploading)
2533 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2534 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2541 int CtdlIPCWriteUpload(CtdlIPC * ipc, FILE * uploadFP, void (*progress_gauge_callback)
2542 (CtdlIPC *, unsigned long, unsigned long), char *cret)
2549 FILE *fd = uploadFP;
2555 fseek(fd, 0L, SEEK_END);
2559 if (progress_gauge_callback)
2560 progress_gauge_callback(ipc, 0, bytes);
2562 while (offset < bytes) {
2565 /* Read some data in */
2566 to_write = fread(buf, 1, 4096, fd);
2568 if (feof(fd) || ferror(fd))
2571 sprintf(aaa, "WRIT %d", (int) to_write);
2572 CtdlIPC_putline(ipc, aaa);
2573 CtdlIPC_getline(ipc, aaa);
2574 strcpy(cret, &aaa[4]);
2576 if (aaa[0] == '7') {
2577 to_write = extract_long(&aaa[4], 0);
2579 serv_write(ipc, buf, to_write);
2581 if (progress_gauge_callback)
2582 progress_gauge_callback(ipc, offset, bytes);
2583 /* Detect short reads and back up if needed */
2584 /* offset will never be negative anyway */
2585 fseek(fd, (signed) offset, SEEK_SET);
2590 if (progress_gauge_callback)
2591 progress_gauge_callback(ipc, 1, 1);
2594 return (!ferr ? ret : -2);
2599 * Generic command method. This method should handle any server command
2600 * except for CHAT. It takes the following arguments:
2602 * ipc The server to speak with
2603 * command Preformatted command to send to server
2604 * to_send A text or binary file to send to server
2605 * (only sent if server requests it)
2606 * bytes_to_send The number of bytes in to_send (required if
2607 * sending binary, optional if sending listing)
2608 * to_receive Pointer to a NULL pointer, if the server
2609 * sends text or binary we will allocate memory
2610 * for the file and stuff it here
2611 * bytes_to_receive If a file is received, we will store its
2613 * proto_response The protocol response. Caller must provide
2614 * this buffer and ensure that it is at least
2615 * 128 bytes in length.
2617 * This function returns a number equal to the protocol response number,
2618 * -1 if an internal error occurred, -2 if caller provided bad values,
2619 * or 0 - the protocol response number if bad values were found during
2620 * the protocol exchange.
2622 * It stores the protocol response string (minus the number) in
2623 * protocol_response as described above. Some commands send additional
2624 * data in this string.
2626 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2627 const char *command,
2628 const char *to_send,
2629 size_t bytes_to_send,
2631 size_t *bytes_to_receive,
2632 char *proto_response
2640 if (!proto_response) {
2645 CtdlIPC_putline(ipc, command);
2647 CtdlIPC_getline(ipc, proto_response);
2648 if (proto_response[3] == '*')
2650 ret = atoi(proto_response);
2651 strcpy(proto_response, &proto_response[4]);
2652 switch (ret / 100) {
2653 default: /* Unknown, punt */
2655 case 3: /* MORE_DATA */
2657 /* Don't need to do anything */
2659 case 1: /* LISTING_FOLLOWS */
2660 if (to_receive && !*to_receive && bytes_to_receive) {
2661 *to_receive = CtdlIPCReadListing(ipc, NULL);
2662 } else { /* Drain */
2663 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2667 case 4: /* SEND_LISTING */
2669 CtdlIPCSendListing(ipc, to_send);
2671 /* No listing given, fake it */
2672 CtdlIPC_putline(ipc, "000");
2676 case 6: /* BINARY_FOLLOWS */
2677 if (to_receive && !*to_receive && bytes_to_receive) {
2678 *bytes_to_receive = extract_long(proto_response, 0);
2679 *to_receive = (char *)
2680 malloc((size_t) * bytes_to_receive);
2684 serv_read(ipc, *to_receive, *bytes_to_receive);
2690 drain = extract_long(proto_response, 0);
2691 while (drain > SIZ) {
2692 serv_read(ipc, buf, SIZ);
2695 serv_read(ipc, buf, drain);
2699 case 7: /* SEND_BINARY */
2700 if (to_send && bytes_to_send) {
2701 serv_write(ipc, to_send, bytes_to_send);
2702 } else if (bytes_to_send) {
2703 /* Fake it, send nulls */
2706 fake = bytes_to_send;
2707 memset(buf, '\0', SIZ);
2708 while (fake > SIZ) {
2709 serv_write(ipc, buf, SIZ);
2712 serv_write(ipc, buf, fake);
2714 } /* else who knows? DANGER WILL ROBINSON */
2716 case 8: /* START_CHAT_MODE */
2717 if (!strncasecmp(command, "CHAT", 4)) {
2718 /* Don't call chatmode with generic! */
2719 CtdlIPC_putline(ipc, "/quit");
2723 /* In this mode we send then receive listing */
2725 CtdlIPCSendListing(ipc, to_send);
2728 /* No listing given, fake it */
2729 CtdlIPC_putline(ipc, "000");
2732 if (to_receive && !*to_receive && bytes_to_receive) {
2733 *to_receive = CtdlIPCReadListing(ipc, NULL);
2736 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000"));
2741 case 9: /* ASYNC_MSG */
2742 /* CtdlIPCDoAsync(ret, proto_response); */
2743 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2749 CtdlIPC_unlock(ipc);
2755 * Connect to a Citadel on a remote host using a TCP/IP socket
2757 static int tcp_connectsock(char *host, char *service) {
2758 struct in6_addr serveraddr;
2759 struct addrinfo hints;
2760 struct addrinfo *res = NULL;
2761 struct addrinfo *ai = NULL;
2765 if ((host == NULL) || IsEmptyStr(host)) {
2766 service = DEFAULT_HOST;
2768 if ((service == NULL) || IsEmptyStr(service)) {
2769 service = DEFAULT_PORT;
2772 memset(&hints, 0x00, sizeof(hints));
2773 hints.ai_flags = AI_NUMERICSERV;
2774 hints.ai_family = AF_UNSPEC;
2775 hints.ai_socktype = SOCK_STREAM;
2778 * Handle numeric IPv4 and IPv6 addresses
2780 rc = inet_pton(AF_INET, host, &serveraddr);
2781 if (rc == 1) { /* dotted quad */
2782 hints.ai_family = AF_INET;
2783 hints.ai_flags |= AI_NUMERICHOST;
2786 rc = inet_pton(AF_INET6, host, &serveraddr);
2787 if (rc == 1) { /* IPv6 address */
2788 hints.ai_family = AF_INET6;
2789 hints.ai_flags |= AI_NUMERICHOST;
2793 /* Begin the connection process */
2795 rc = getaddrinfo(host, service, &hints, &res);
2801 * Try all available addresses until we connect to one or until we run out.
2803 for (ai = res; ai != NULL; ai = ai->ai_next) {
2804 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2808 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2810 return (sock); /* Connected! */
2813 close(sock); /* Failed. Close the socket to avoid fd leak! */
2821 * Connect to a Citadel on the local host using a unix domain socket
2823 static int uds_connectsock(int *isLocal, char *sockpath) {
2824 struct sockaddr_un addr;
2827 memset(&addr, 0, sizeof(addr));
2828 addr.sun_family = AF_UNIX;
2829 strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2831 s = socket(AF_UNIX, SOCK_STREAM, 0);
2836 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2847 * input binary data from socket
2849 static void serv_read(CtdlIPC * ipc, char *buf, unsigned int bytes) {
2850 unsigned int len, rlen;
2852 #if defined(HAVE_OPENSSL)
2854 serv_read_ssl(ipc, buf, bytes);
2859 while (len < bytes) {
2860 rlen = read(ipc->sock, &buf[len], bytes - len);
2862 connection_died(ipc, 0);
2871 * send binary to server
2873 void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes) {
2874 unsigned int bytes_written = 0;
2877 #if defined(HAVE_OPENSSL)
2879 serv_write_ssl(ipc, buf, nbytes);
2883 while (bytes_written < nbytes) {
2884 retval = write(ipc->sock, &buf[bytes_written], nbytes - bytes_written);
2886 connection_died(ipc, 0);
2889 bytes_written += retval;
2896 * input binary data from encrypted connection
2898 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes) {
2903 while (len < bytes) {
2904 if (SSL_want_read(ipc->ssl)) {
2905 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2906 error_printf("SSL_write in serv_read:\n");
2907 ERR_print_errors_fp(stderr);
2910 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2914 errval = SSL_get_error(ipc->ssl, rlen);
2915 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
2919 error_printf("SSL_read in serv_read: %s\n", ERR_reason_error_string(ERR_peek_error()));
2920 connection_died(ipc, 1);
2929 * send binary to server encrypted
2931 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes) {
2932 unsigned int bytes_written = 0;
2936 while (bytes_written < nbytes) {
2937 if (SSL_want_write(ipc->ssl)) {
2938 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2939 error_printf("SSL_read in serv_write:\n");
2940 ERR_print_errors_fp(stderr);
2943 retval = SSL_write(ipc->ssl, &buf[bytes_written], nbytes - bytes_written);
2947 errval = SSL_get_error(ipc->ssl, retval);
2948 if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
2952 if (errval == SSL_ERROR_ZERO_RETURN || errval == SSL_ERROR_SSL) {
2953 serv_write(ipc, &buf[bytes_written], nbytes - bytes_written);
2956 error_printf("SSL_write in serv_write: %s\n", ERR_reason_error_string(ERR_peek_error()));
2957 connection_died(ipc, 1);
2960 bytes_written += retval;
2965 static void CtdlIPC_init_OpenSSL(void) {
2967 const SSL_METHOD *ssl_method;
2970 /* already done init */
2979 SSL_load_error_strings();
2980 SSLeay_add_ssl_algorithms();
2982 /* Set up the SSL context in which we will oeprate */
2983 ssl_method = SSLv23_client_method();
2984 ssl_ctx = SSL_CTX_new(ssl_method);
2986 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2989 /* Any reasonable cipher we can get */
2990 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2991 error_printf("No ciphers available for encryption\n");
2994 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2996 /* Load DH parameters into the context */
2999 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
3003 if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
3004 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
3009 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
3013 #endif /* HAVE_OPENSSL */
3016 int ReadNetworkChunk(CtdlIPC * ipc) {
3030 FD_SET(ipc->sock, &read_fd);
3031 ret = select(ipc->sock + 1, &read_fd, NULL, NULL, &tv);
3035 *(ipc->BufPtr) = '\0';
3036 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
3038 ipc->BufPtr[n] = '\0';
3047 if (!(errno == EINTR || errno == EAGAIN))
3048 error_printf("\nselect failed: %d %s\n", err, strerror(err));
3056 * input string from socket - implemented in terms of serv_read()
3060 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf) {
3062 char *aptr, *bptr, *aeptr, *beptr;
3066 #if defined(HAVE_OPENSSL)
3069 /* Read one character at a time. */
3071 serv_read(ipc, &buf[i], 1);
3072 if (buf[i] == '\n' || i == (SIZ - 1))
3076 /* If we got a long line, discard characters until the newline. */
3078 while (buf[i] != '\n')
3079 serv_read(ipc, &buf[i], 1);
3081 /* Strip the trailing newline (and carriage return, if present) */
3082 if (i >= 0 && buf[i] == 10)
3084 if (i >= 0 && buf[i] == 13)
3089 if (ipc->Buf == NULL) {
3091 ipc->Buf = (char *) malloc(ipc->BufSize + 10);
3093 ipc->BufPtr = ipc->Buf;
3097 if (ipc->BufUsed == 0)
3098 ReadNetworkChunk(ipc);
3103 aeptr = ipc->Buf + ipc->BufSize;
3104 while ((aptr < aeptr) && (bptr < beptr) && (*aptr != '\0') && (*aptr != '\n'))
3105 *(bptr++) = *(aptr++);
3106 if ((*aptr == '\n') && (aptr < aeptr)) {
3107 /* Terminate it right, remove the line breaks */
3108 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3110 while ((aptr < aeptr) && (*(aptr + 1) == '\0'))
3113 if ((bptr > buf + 1) && (*(bptr - 1) == '\r'))
3116 /* is there more in the buffer we need to read later? */
3117 if (ipc->Buf + ipc->BufUsed > aptr) {
3121 ipc->BufPtr = ipc->Buf;
3126 // should we move our read stuf to the bufferstart so we have more space at the end?
3127 else if ((ipc->BufPtr != ipc->Buf) && (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4)))) {
3128 size_t NewBufSize = ipc->BufSize * 2;
3129 int delta = (ipc->BufPtr - ipc->Buf);
3132 /* if the line would end after our buffer, we should use a bigger buffer. */
3133 NewBuf = (char *) malloc(NewBufSize + 10);
3134 memcpy(NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3136 ipc->Buf = ipc->BufPtr = NewBuf;
3137 ipc->BufUsed -= delta;
3138 ipc->BufSize = NewBufSize;
3140 if (ReadNetworkChunk(ipc) < 0) {
3147 #else /* CHUNKED_READ */
3149 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf) {
3152 /* Read one character at a time. */
3154 serv_read(ipc, &buf[i], 1);
3155 if (buf[i] == '\n' || i == (SIZ - 1))
3159 /* If we got a long line, discard characters until the newline. */
3161 while (buf[i] != '\n')
3162 serv_read(ipc, &buf[i], 1);
3164 /* Strip the trailing newline (and carriage return, if present) */
3165 if (i >= 0 && buf[i] == 10)
3167 if (i >= 0 && buf[i] == 13)
3172 #endif /* CHUNKED_READ */
3175 void CtdlIPC_chat_recv(CtdlIPC * ipc, char *buf) {
3176 CtdlIPC_getline(ipc, buf);
3180 * send line to server - implemented in terms of serv_write()
3182 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf) {
3187 cmd = malloc(len + 2);
3189 /* This requires no extra memory */
3190 serv_write(ipc, buf, len);
3191 serv_write(ipc, "\n", 1);
3194 /* This is network-optimized */
3195 strncpy(cmd, buf, len);
3196 strcpy(cmd + len, "\n");
3197 serv_write(ipc, cmd, len + 1);
3201 ipc->last_command_sent = time(NULL);
3205 void CtdlIPC_chat_send(CtdlIPC * ipc, const char *buf) {
3206 CtdlIPC_putline(ipc, buf);
3213 CtdlIPC *CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf) {
3220 ipc = malloc(sizeof(struct _CtdlIPC));
3224 #if defined(HAVE_OPENSSL)
3226 CtdlIPC_init_OpenSSL();
3228 ipc->sock = -1; /* Not connected */
3229 ipc->isLocal = 0; /* Not local, of course! */
3230 ipc->downloading = 0;
3232 ipc->last_command_sent = 0L;
3233 ipc->network_status_cb = NULL;
3238 strcpy(cithost, DEFAULT_HOST); /* default host */
3239 strcpy(citport, DEFAULT_PORT); /* default port */
3241 /* Allow caller to supply our values */
3242 if (hostbuf && strlen(hostbuf) > 0) {
3243 strcpy(cithost, hostbuf);
3245 if (portbuf && strlen(portbuf) > 0) {
3246 strcpy(citport, portbuf);
3249 /* Read host/port from command line if present */
3250 for (a = 0; a < argc; ++a) {
3255 strcpy(cithost, argv[a]);
3258 strcpy(citport, argv[a]);
3261 error_printf("%s: usage: ", argv[0]);
3262 error_printf("%s [host] [port] ", argv[0]);
3269 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3273 /* If we're using a unix domain socket we can do a bunch of stuff */
3274 if (!strcmp(cithost, UDS)) {
3275 if (!strcasecmp(citport, DEFAULT_PORT)) {
3276 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3279 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3281 printf("[%s]\n", sockpath);
3282 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3283 if (ipc->sock == -1) {
3287 if (hostbuf != NULL)
3288 strcpy(hostbuf, cithost);
3289 if (portbuf != NULL)
3290 strcpy(portbuf, sockpath);
3291 strcpy(ipc->ip_hostname, "");
3292 strcpy(ipc->ip_address, "");
3296 printf("[%s:%s]\n", cithost, citport);
3297 ipc->sock = tcp_connectsock(cithost, citport);
3298 if (ipc->sock == -1) {
3304 /* Learn the actual network identity of the host to which we are connected */
3306 struct sockaddr_in6 clientaddr;
3307 unsigned int addrlen = sizeof(clientaddr);
3309 ipc->ip_hostname[0] = 0;
3310 ipc->ip_address[0] = 0;
3312 getpeername(ipc->sock, (struct sockaddr *) &clientaddr, &addrlen);
3313 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0);
3314 getnameinfo((struct sockaddr *) &clientaddr, addrlen, ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST);
3316 /* stuff other things elsewhere */
3318 if (hostbuf != NULL)
3319 strcpy(hostbuf, cithost);
3320 if (portbuf != NULL)
3321 strcpy(portbuf, citport);
3327 * Disconnect and delete the IPC class (destructor)
3329 void CtdlIPC_delete(CtdlIPC *ipc) {
3332 SSL_shutdown(ipc->ssl);
3337 if (ipc->sock > -1) {
3338 shutdown(ipc->sock, 2); /* Close it up */
3341 if (ipc->Buf != NULL)
3350 * Disconnect and delete the IPC class (destructor)
3351 * Also NULLs out the pointer
3353 void CtdlIPC_delete_ptr(CtdlIPC **pipc) {
3354 CtdlIPC_delete(*pipc);