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) ipc->network_status_cb(1);
60 void CtdlIPC_unlock(CtdlIPC *ipc)
62 if (ipc->network_status_cb) ipc->network_status_cb(0);
70 char *libcitadelclient_version_string(void) {
71 return "libcitadelclient(unnumbered)";
77 #define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
78 snprintf(SUBDIR,sizeof SUBDIR, "%s%s%s%s%s%s%s", \
79 (home&!relh)?ctdl_home_directory:basedir, \
80 ((basedir!=ctdldir)&(home&!relh))?basedir:"/", \
81 ((basedir!=ctdldir)&(home&!relh))?"/":"", \
83 (relhome[0]!='\0')?"/":"",\
85 (dirbuffer[0]!='\0')?"/":"");
87 #define DBG_PRINT(A) if (dbg==1) fprintf (stderr,"%s : %s \n", #A, A)
90 void calc_dirs_n_files(int relh, int home, const char *relhome, char *ctdldir, int dbg)
92 const char* basedir = "";
93 char dirbuffer[PATH_MAX] = "";
95 StripSlashes(ctdldir, 1);
102 COMPUTE_DIRECTORY(ctdl_run_dir);
103 StripSlashes(ctdl_run_dir, 1);
106 #ifndef HAVE_AUTO_ETC_DIR
109 basedir=AUTO_ETC_DIR;
111 COMPUTE_DIRECTORY(ctdl_autoetc_dir);
112 StripSlashes(ctdl_autoetc_dir, 1);
120 COMPUTE_DIRECTORY(ctdl_etc_dir);
121 StripSlashes(ctdl_etc_dir, 1);
125 snprintf(file_citadel_rc,
126 sizeof file_citadel_rc,
129 StripSlashes(file_citadel_rc, 0);
131 snprintf(file_citadel_socket,
132 sizeof file_citadel_socket,
135 StripSlashes(file_citadel_socket, 0);
137 DBG_PRINT(ctdl_run_dir);
138 DBG_PRINT(file_citadel_socket);
139 DBG_PRINT(ctdl_etc_dir);
140 DBG_PRINT(file_citadel_rc);
143 void setCryptoStatusHook(void (*hook)(char *s)) {
147 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
148 ipc->network_status_cb = hook;
152 char instant_msgs = 0;
155 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
156 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
158 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
159 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
160 static void endtls(SSL *ssl);
161 #endif /* HAVE_OPENSSL */
162 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
163 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
167 const char *svn_revision(void);
170 * Does nothing. The server should always return 200.
172 int CtdlIPCNoop(CtdlIPC *ipc)
176 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
181 * Does nothing interesting. The server should always return 200
182 * along with your string.
184 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
190 if (!cret) return -2;
192 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
195 sprintf(aaa, "ECHO %s", arg);
196 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
203 * Asks the server to close the connecction.
204 * Should always return 200.
206 int CtdlIPCQuit(CtdlIPC *ipc)
208 int ret = 221; /* Default to successful quit */
212 if (ipc->sock > -1) {
213 CtdlIPC_putline(ipc, "QUIT");
214 CtdlIPC_getline(ipc, aaa);
219 SSL_shutdown(ipc->ssl);
223 shutdown(ipc->sock, 2); /* Close connection; we're dead */
231 * Asks the server to log out. Should always return 200, even if no user
232 * was logged in. The user will not be logged in after this!
234 int CtdlIPCLogout(CtdlIPC *ipc)
240 CtdlIPC_putline(ipc, "LOUT");
241 CtdlIPC_getline(ipc, aaa);
249 * First stage of authentication - pass the username. Returns 300 if the
250 * username is able to log in, with the username correctly spelled in cret.
251 * Returns various 500 error codes if the user doesn't exist, etc.
253 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
258 if (!username) return -2;
259 if (!cret) return -2;
261 aaa = (char *)malloc((size_t)(strlen(username) + 6));
264 sprintf(aaa, "USER %s", username);
265 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
272 * Second stage of authentication - provide password. The server returns
273 * 200 and several arguments in cret relating to the user's account.
275 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
280 if (!passwd) return -2;
281 if (!cret) return -2;
283 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
286 sprintf(aaa, "PASS %s", passwd);
287 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
294 * Second stage of authentication - provide password. The server returns
295 * 200 and several arguments in cret relating to the user's account.
297 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
302 if (!response) return -2;
303 if (!cret) return -2;
305 aaa = (char *)malloc((size_t)(strlen(response) + 6));
308 sprintf(aaa, "PAS2 %s", response);
309 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
316 * Create a new user. This returns 200 plus the same arguments as TryPassword
317 * if selfservice is nonzero, unless there was a problem creating the account.
318 * If selfservice is zero, creates a new user but does not log out the existing
319 * user - intended for use by system administrators to create accounts on
320 * behalf of other users.
322 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
327 if (!username) return -2;
328 if (!cret) return -2;
330 aaa = (char *)malloc((size_t)(strlen(username) + 6));
333 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
334 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
341 * Changes the user's password. Returns 200 if changed, errors otherwise.
343 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
348 if (!passwd) return -2;
349 if (!cret) return -2;
351 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
354 sprintf(aaa, "SETP %s", passwd);
355 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
362 /* Caller must free the march list */
363 /* Room types are defined in enum RoomList; keep these in sync! */
364 /* floor is -1 for all, or floornum */
365 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
368 struct march *march = NULL;
369 static char *proto[] =
370 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
375 if (!listing) return -2;
376 if (*listing) return -2; /* Free the listing first */
377 if (!cret) return -2;
378 /* if (which < 0 || which > 4) return -2; */
379 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
381 sprintf(aaa, "%s %d", proto[which], floor);
382 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
383 if (ret / 100 == 1) {
386 while (bbb && strlen(bbb)) {
389 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
391 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
392 mptr = (struct march *) malloc(sizeof (struct march));
395 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
396 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
397 mptr->march_floor = (char) extract_int(aaa, 2);
398 mptr->march_order = (char) extract_int(aaa, 3);
399 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
400 mptr->march_access = (char) extract_int(aaa, 5);
407 while (mptr2->next != NULL)
421 /* Caller must free the struct ctdluser; caller may pass an existing one */
422 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
426 if (!cret) return -2;
427 if (!uret) return -2;
428 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
429 if (!*uret) return -1;
431 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
432 if (ret / 100 == 2) {
433 uret[0]->flags = extract_int(cret, 2);
440 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
444 if (!uret) return -2;
445 if (!cret) return -2;
451 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
456 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
461 if (!oldname) return -2;
462 if (!newname) return -2;
463 if (!cret) return -2;
465 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
466 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
472 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
473 struct ctdlipcroom **rret, char *cret)
478 if (!cret) return -2;
479 if (!rret) return -2;
480 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
481 if (!*rret) return -1;
484 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
489 sprintf(aaa, "GOTO %s|%s", room, passwd);
491 aaa = (char *)malloc(strlen(room) + 6);
496 sprintf(aaa, "GOTO %s", room);
498 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
499 if (ret / 100 == 2) {
500 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
501 rret[0]->RRunread = extract_long(cret, 1);
502 rret[0]->RRtotal = extract_long(cret, 2);
503 rret[0]->RRinfoupdated = extract_int(cret, 3);
504 rret[0]->RRflags = extract_int(cret, 4);
505 rret[0]->RRhighest = extract_long(cret, 5);
506 rret[0]->RRlastread = extract_long(cret, 6);
507 rret[0]->RRismailbox = extract_int(cret, 7);
508 rret[0]->RRaide = extract_int(cret, 8);
509 rret[0]->RRnewmail = extract_long(cret, 9);
510 rret[0]->RRfloor = extract_int(cret, 10);
511 rret[0]->RRcurrentview = extract_int(cret, 11);
512 rret[0]->RRdefaultview = extract_int(cret, 12);
513 /* position 13 is a trash folder flag ... irrelevant in this client */
514 rret[0]->RRflags2 = extract_int(cret, 14);
525 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
526 /* whicharg is number of messages, applies to last, first, gt, lt */
527 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
528 const char *mtemplate, unsigned long **mret, char *cret)
531 unsigned long count = 0;
532 static char *proto[] =
533 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
538 if (!cret) return -2;
539 if (!mret) return -2;
540 if (*mret) return -2;
541 if (which < 0 || which > 6) return -2;
544 sprintf(aaa, "MSGS %s||%d", proto[which],
545 (mtemplate) ? 1 : 0);
547 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
548 (mtemplate) ? 1 : 0);
549 if (mtemplate) count = strlen(mtemplate);
550 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
554 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
557 while (bbb && strlen(bbb)) {
558 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
559 remove_token(bbb, 0, '\n');
560 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
561 sizeof (unsigned long)));
563 (*mret)[count++] = atol(aaa);
575 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
576 struct ctdlipcmessage **mret, char *cret)
582 int multipart_hunting = 0;
583 char multipart_prefix[128];
586 if (!cret) return -1;
587 if (!mret) return -1;
588 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
589 if (!*mret) return -1;
590 if (!msgnum) return -1;
592 strcpy(encoding, "");
593 strcpy(mret[0]->content_type, "");
594 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
595 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
596 if (ret / 100 == 1) {
598 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
599 while (strlen(bbb) > 4 && bbb[4] == '=') {
600 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
601 remove_token(bbb, 0, '\n');
603 if (!strncasecmp(aaa, "nhdr=yes", 8))
605 else if (!strncasecmp(aaa, "from=", 5))
606 safestrncpy(mret[0]->author, &aaa[5], SIZ);
607 else if (!strncasecmp(aaa, "type=", 5))
608 mret[0]->type = atoi(&aaa[5]);
609 else if (!strncasecmp(aaa, "msgn=", 5))
610 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
611 else if (!strncasecmp(aaa, "subj=", 5))
612 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
613 else if (!strncasecmp(aaa, "rfca=", 5))
614 safestrncpy(mret[0]->email, &aaa[5], SIZ);
615 else if (!strncasecmp(aaa, "room=", 5))
616 safestrncpy(mret[0]->room, &aaa[5], SIZ);
617 else if (!strncasecmp(aaa, "rcpt=", 5))
618 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
619 else if (!strncasecmp(aaa, "wefw=", 5))
620 safestrncpy(mret[0]->references, &aaa[5], SIZ);
621 else if (!strncasecmp(aaa, "time=", 5))
622 mret[0]->time = atol(&aaa[5]);
624 /* Multipart/alternative prefix & suffix strings help
625 * us to determine which part we want to download.
627 else if (!strncasecmp(aaa, "pref=", 5)) {
628 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
629 if (!strcasecmp(multipart_prefix,
630 "multipart/alternative")) {
634 else if (!strncasecmp(aaa, "suff=", 5)) {
635 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
636 if (!strcasecmp(multipart_prefix,
637 "multipart/alternative")) {
642 else if (!strncasecmp(aaa, "part=", 5)) {
643 struct parts *ptr, *chain;
645 ptr = (struct parts *)calloc(1, sizeof (struct parts));
648 /* Fill the buffers for the caller */
649 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
650 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
651 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
652 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
653 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
654 ptr->length = extract_long(&aaa[5], 5);
655 if (!mret[0]->attachments)
656 mret[0]->attachments = ptr;
658 chain = mret[0]->attachments;
664 /* Now handle multipart/alternative */
665 if (multipart_hunting > 0) {
666 if ( (!strcasecmp(ptr->mimetype,
668 || (!strcasecmp(ptr->mimetype,
670 strcpy(mret[0]->mime_chosen,
678 /* Eliminate "text\n" */
679 remove_token(bbb, 0, '\n');
681 /* If doing a MIME thing, pull out the extra headers */
684 if (!strncasecmp(bbb, "Content-type:", 13)) {
685 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
686 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
687 striplt(mret[0]->content_type);
689 /* strip out ";charset=" portion. FIXME do something with
690 * the charset (like... convert it) instead of just throwing
693 if (strstr(mret[0]->content_type, ";") != NULL) {
694 strcpy(strstr(mret[0]->content_type, ";"), "");
698 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
699 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
700 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
701 striplt(mret[0]->mime_chosen);
703 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
704 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
705 strcpy(encoding, &encoding[26]);
708 remove_token(bbb, 0, '\n');
709 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
710 remove_token(bbb, 0, '\n');
717 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
719 int bytes_decoded = 0;
720 ccc = malloc(strlen(bbb) + 32768);
721 if (!strcasecmp(encoding, "base64")) {
722 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
724 else if (!strcasecmp(encoding, "quoted-printable")) {
725 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
727 ccc[bytes_decoded] = 0;
732 /* FIXME: Strip trailing whitespace */
733 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
736 bbb = (char *)realloc(bbb, 1);
746 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
751 if (!cret) return -2;
752 if (!listing) return -2;
753 if (*listing) return -2;
755 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
761 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
765 char *listing = NULL;
768 if (!cret) return -2;
770 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
771 if (ret / 100 == 1) {
774 while (*listing && strlen(listing)) {
775 extract_token(buf, listing, 0, '\n', sizeof buf);
776 remove_token(listing, 0, '\n');
778 case 0: ipc->ServInfo.pid = atoi(buf);
780 case 1: strcpy(ipc->ServInfo.nodename,buf);
782 case 2: strcpy(ipc->ServInfo.humannode,buf);
784 case 3: strcpy(ipc->ServInfo.fqdn,buf);
786 case 4: strcpy(ipc->ServInfo.software,buf);
788 case 5: ipc->ServInfo.rev_level = atoi(buf);
790 case 6: strcpy(ipc->ServInfo.site_location,buf);
792 case 7: strcpy(ipc->ServInfo.sysadm,buf);
794 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
796 case 10: ipc->ServInfo.ok_floors = atoi(buf);
798 case 11: ipc->ServInfo.paging_level = atoi(buf);
800 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
802 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
804 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
806 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
808 case 17: ipc->ServInfo.load_avg = atof(buf);
810 case 18: ipc->ServInfo.worker_avg = atof(buf);
812 case 19: ipc->ServInfo.thread_count = atoi(buf);
814 case 20: ipc->ServInfo.has_sieve = atoi(buf);
816 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
818 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
820 case 24: ipc->ServInfo.guest_logins = atoi(buf);
826 if (listing) free(listing);
832 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
837 if (!cret) return -2;
838 if (!listing) return -2;
839 if (*listing) return -2;
841 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
847 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
849 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
854 if (!cret) return -2;
857 sprintf(aaa, "SLRP %ld", msgnum);
860 sprintf(aaa, "SLRP HIGHEST");
862 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
868 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
873 if (!cret) return -2;
874 if (!username) return -2;
876 aaa = (char *)malloc(strlen(username) + 6);
879 sprintf(aaa, "INVT %s", username);
880 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
887 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
892 if (!cret) return -1;
893 if (!username) return -1;
895 aaa = (char *)malloc(strlen(username) + 6);
897 sprintf(aaa, "KICK %s", username);
898 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
905 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
909 if (!cret) return -2;
910 if (!qret) return -2;
911 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
912 if (!*qret) return -1;
914 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
915 if (ret / 100 == 2) {
916 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
917 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
918 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
919 qret[0]->QRflags = extract_int(cret, 3);
920 qret[0]->QRfloor = extract_int(cret, 4);
921 qret[0]->QRorder = extract_int(cret, 5);
922 qret[0]->QRdefaultview = extract_int(cret, 6);
923 qret[0]->QRflags2 = extract_int(cret, 7);
930 /* set forget to kick all users out of room */
931 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
936 if (!cret) return -2;
937 if (!qret) return -2;
939 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
940 strlen(qret->QRdirname) + 64);
943 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
944 qret->QRname, qret->QRpasswd, qret->QRdirname,
945 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
946 qret->QRdefaultview, qret->QRflags2);
947 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
954 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
956 if (!cret) return -1;
958 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
963 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
968 if (!cret) return -2;
969 if (!username) return -2;
971 aaa = (char *)malloc(strlen(username) + 6);
974 sprintf(aaa, "SETA %s", username);
975 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
982 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
988 if (!cret) return -2;
991 if (mr->references) {
992 for (ptr=mr->references; *ptr != 0; ++ptr) {
993 if (*ptr == '|') *ptr = '!';
997 snprintf(cmd, sizeof cmd,
998 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
999 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
1000 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
1002 if ((flag == 0) && (subject_required != NULL)) {
1003 /* Is the server strongly recommending that the user enter a message subject? */
1004 if ((cret[3] != '\0') && (cret[4] != '\0')) {
1005 *subject_required = extract_int(&cret[4], 1);
1015 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
1019 if (!cret) return -2;
1020 if (!iret) return -2;
1021 if (*iret) return -2;
1023 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
1028 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
1032 if (!cret) return -2;
1033 if (!msgnum) return -2;
1035 sprintf(aaa, "DELE %ld", msgnum);
1036 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1041 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
1046 if (!cret) return -2;
1047 if (!destroom) return -2;
1048 if (!msgnum) return -2;
1050 aaa = (char *)malloc(strlen(destroom) + 28);
1051 if (!aaa) return -1;
1053 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
1054 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1061 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
1065 if (!cret) return -2;
1067 sprintf(aaa, "KILL %d", for_real);
1068 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1073 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
1074 const char *password, int floor, char *cret)
1079 if (!cret) return -2;
1080 if (!roomname) return -2;
1083 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
1084 if (!aaa) return -1;
1085 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
1088 aaa = (char *)malloc(strlen(roomname) + 40);
1089 if (!aaa) return -1;
1090 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1093 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1100 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1102 if (!cret) return -2;
1104 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1109 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1115 if (!cret) return -2;
1116 if (!mret) return -2;
1117 if (*mret) return -2;
1118 if (!message) return -2;
1120 aaa = (char *)malloc(strlen(message) + 6);
1121 if (!aaa) return -1;
1123 sprintf(aaa, "MESG %s", message);
1124 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1131 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1133 if (!cret) return -2;
1135 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1140 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1146 if (!cret) return -2;
1147 if (!rret) return -2;
1148 if (*rret) return -2;
1151 aaa = (char *)malloc(strlen(username) + 6);
1153 aaa = (char *)malloc(12);
1154 if (!aaa) return -1;
1157 sprintf(aaa, "GREG %s", username);
1159 sprintf(aaa, "GREG _SELF_");
1160 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1167 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1172 if (!cret) return -2;
1173 if (!username) return -2;
1174 if (axlevel < AxDeleted || axlevel > AxAideU) return -2;
1176 aaa = (char *)malloc(strlen(username) + 17);
1177 if (!aaa) return -1;
1179 sprintf(aaa, "VALI %s|%d", username, axlevel);
1180 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1187 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1191 if (!cret) return -1;
1192 if (!info) return -1;
1194 sprintf(aaa, "EINF %d", for_real);
1195 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1200 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1206 if (!cret) return -1;
1207 if (!listing) return -1;
1208 if (*listing) return -1;
1209 if (!searchstring) return -1;
1211 cmd = malloc(strlen(searchstring) + 10);
1212 sprintf(cmd, "LIST %s", searchstring);
1214 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1221 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1223 if (!cret) return -1;
1224 if (!info) return -1;
1226 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1232 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1236 if (!cret) return -1;
1237 if (!chek) return -1;
1239 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1240 if (ret / 100 == 2) {
1241 chek->newmail = extract_long(cret, 0);
1242 chek->needregis = extract_int(cret, 1);
1243 chek->needvalid = extract_int(cret, 2);
1250 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1255 if (!cret) return -2;
1256 if (!filename) return -2;
1258 aaa = (char *)malloc(strlen(filename) + 6);
1259 if (!aaa) return -1;
1261 sprintf(aaa, "DELF %s", filename);
1262 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1269 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1274 if (!cret) return -2;
1275 if (!filename) return -2;
1276 if (!destroom) return -2;
1278 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1279 if (!aaa) return -1;
1281 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1282 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1289 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1294 if (!cret) return -1;
1295 if (!listing) return -1;
1296 if (*listing) return -1;
1298 *stamp = CtdlIPCServerTime(ipc, cret);
1300 *stamp = time(NULL);
1301 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1307 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1309 void (*progress_gauge_callback)
1310 (CtdlIPC*, unsigned long, unsigned long),
1319 if (!cret) return -2;
1320 if (!filename) return -2;
1321 if (!buf) return -2;
1322 if (*buf) return -2;
1323 if (ipc->downloading) return -2;
1325 aaa = (char *)malloc(strlen(filename) + 6);
1326 if (!aaa) return -1;
1328 sprintf(aaa, "OPEN %s", filename);
1329 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1331 if (ret / 100 == 2) {
1332 ipc->downloading = 1;
1333 bytes = extract_long(cret, 0);
1334 last_mod = extract_int(cret, 1);
1335 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1337 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1338 progress_gauge_callback, cret);
1340 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1341 progress_gauge_callback, cret);
1344 ret = CtdlIPCEndDownload(ipc, cret);
1346 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1347 filename, mimetype);
1354 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1356 void (*progress_gauge_callback)
1357 (CtdlIPC*, unsigned long, unsigned long),
1367 if (!cret) return -2;
1368 if (!buf) return -2;
1369 if (*buf) return -2;
1370 if (!part) return -2;
1371 if (!msgnum) return -2;
1372 if (ipc->downloading) return -2;
1374 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1375 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1376 if (ret / 100 == 2) {
1377 ipc->downloading = 1;
1378 bytes = extract_long(cret, 0);
1379 last_mod = extract_int(cret, 1);
1380 extract_token(filename, cret, 2, '|', sizeof filename);
1381 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1382 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1383 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1384 ret = CtdlIPCEndDownload(ipc, cret);
1386 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1387 filename, mimetype);
1394 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1395 void (*progress_gauge_callback)
1396 (CtdlIPC*, unsigned long, unsigned long),
1405 if (!cret) return -1;
1406 if (!buf) return -1;
1407 if (*buf) return -1;
1408 if (!filename) return -1;
1409 if (ipc->downloading) return -1;
1411 aaa = (char *)malloc(strlen(filename) + 6);
1412 if (!aaa) return -1;
1414 sprintf(aaa, "OIMG %s", filename);
1415 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1417 if (ret / 100 == 2) {
1418 ipc->downloading = 1;
1419 bytes = extract_long(cret, 0);
1420 last_mod = extract_int(cret, 1);
1421 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1422 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1423 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1424 ret = CtdlIPCEndDownload(ipc, cret);
1426 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1427 filename, mimetype);
1434 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1436 void (*progress_gauge_callback)
1437 (CtdlIPC*, unsigned long, unsigned long),
1443 char MimeTestBuf[64];
1444 const char *MimeType;
1447 if (!cret) return -1;
1448 if (!save_as) return -1;
1449 if (!comment) return -1;
1450 if (!path) return -1;
1451 if (!*path) return -1;
1452 if (ipc->uploading) return -1;
1454 uploadFP = fopen(path, "r");
1455 if (!uploadFP) return -2;
1457 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1462 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1463 aaa = (char *)malloc(strlen(save_as) + strlen(MimeType) + strlen(comment) + 7);
1464 if (!aaa) return -1;
1466 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1467 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1469 if (ret / 100 == 2) {
1471 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1472 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1480 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1481 const char *save_as,
1482 void (*progress_gauge_callback)
1483 (CtdlIPC*, unsigned long, unsigned long),
1489 char MimeTestBuf[64];
1490 const char *MimeType;
1493 if (!cret) return -1;
1494 if (!save_as) return -1;
1495 if (!path && for_real) return -1;
1496 if (!*path && for_real) return -1;
1497 if (ipc->uploading) return -1;
1499 aaa = (char *)malloc(strlen(save_as) + 17);
1500 if (!aaa) return -1;
1502 uploadFP = fopen(path, "r");
1503 if (!uploadFP) return -2;
1505 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1509 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1511 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1512 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1514 if (ret / 100 == 2 && for_real) {
1516 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1517 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1525 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1530 if (!cret) return -2;
1531 if (!username) return -2;
1533 aaa = (char *)malloc(strlen(username) + 6);
1534 if (!aaa) return -1;
1536 sprintf(aaa, "QUSR %s", username);
1537 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1544 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1548 if (!cret) return -2;
1549 if (!listing) return -2;
1550 if (*listing) return -2;
1552 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1557 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1562 if (!cret) return -2;
1563 if (!name) return -2;
1565 sprintf(aaa, "CFLR %s|%d", name, for_real);
1566 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1572 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1576 if (!cret) return -1;
1577 if (floornum < 0) return -1;
1579 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1580 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1585 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1590 if (!cret) return -2;
1591 if (!floorname) return -2;
1592 if (floornum < 0) return -2;
1594 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1595 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1603 * You only need to fill out hostname, the defaults will be used if any of the
1604 * other fields are not set properly.
1606 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1607 int revision, const char *software_name, const char *hostname,
1613 if (developerid < 0 || clientid < 0 || revision < 0 ||
1617 revision = CLIENT_VERSION - 600;
1618 software_name = "Citadel (libcitadel)";
1620 if (!hostname) return -2;
1622 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1623 if (!aaa) return -1;
1625 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1626 revision, software_name, hostname);
1627 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1634 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1640 if (!cret) return -2;
1641 if (!username) return -2;
1643 aaa = (char *)malloc(strlen(username) + 8);
1644 if (!aaa) return -1;
1647 sprintf(aaa, "SEXP %s|-", username);
1648 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1651 sprintf(aaa, "SEXP %s||", username);
1652 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1660 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1664 if (!cret) return -2;
1665 if (!listing) return -2;
1666 if (*listing) return -2;
1668 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1673 /* mode is 0 = enable, 1 = disable, 2 = status */
1674 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1678 if (!cret) return -2;
1680 sprintf(aaa, "DEXP %d", mode);
1681 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1686 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1688 if (!cret) return -2;
1689 if (!bio) return -2;
1691 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1697 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1703 if (!cret) return -2;
1704 if (!username) return -2;
1705 if (!listing) return -2;
1706 if (*listing) return -2;
1708 aaa = (char *)malloc(strlen(username) + 6);
1709 if (!aaa) return -1;
1711 sprintf(aaa, "RBIO %s", username);
1712 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1719 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1723 if (!cret) return -2;
1724 if (!listing) return -2;
1725 if (*listing) return -2;
1727 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1732 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1736 if (!cret) return -1;
1738 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1739 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1744 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1748 if (!cret) return -1;
1750 sprintf(aaa, "TERM %d", sid);
1751 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1756 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1758 if (!cret) return -1;
1760 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1765 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1769 if (!cret) return -1;
1771 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1772 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1777 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1783 if (!cret) return -2;
1784 if (!text) return -2;
1785 if (!filename) return -2;
1787 aaa = (char *)malloc(strlen(filename) + 6);
1788 if (!aaa) return -1;
1790 sprintf(aaa, "EMSG %s", filename);
1791 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1798 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1803 if (!cret) return -2;
1804 if (!hostname) return -2;
1806 aaa = (char *)malloc(strlen(hostname) + 6);
1807 if (!aaa) return -1;
1809 sprintf(aaa, "HCHG %s", hostname);
1810 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1817 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1822 if (!cret) return -2;
1823 if (!roomname) return -2;
1825 aaa = (char *)malloc(strlen(roomname) + 6);
1826 if (!aaa) return -1;
1828 sprintf(aaa, "RCHG %s", roomname);
1829 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1836 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1841 if (!cret) return -2;
1842 if (!username) return -2;
1844 aaa = (char *)malloc(strlen(username) + 6);
1845 if (!aaa) return -1;
1847 sprintf(aaa, "UCHG %s", username);
1848 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1855 /* This function returns the actual server time reported, or 0 if error */
1856 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1861 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1862 if (ret / 100 == 2) {
1863 tret = extract_long(cret, 0);
1872 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who, struct ctdluser **uret, char *cret)
1877 if (!cret) return -2;
1878 if (!uret) return -2;
1879 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1880 if (!*uret) return -1;
1882 sprintf(aaa, "AGUP %s", who);
1883 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1885 if (ret / 100 == 2) {
1886 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1887 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1888 uret[0]->flags = extract_int(cret, 2);
1889 uret[0]->timescalled = extract_long(cret, 3);
1890 uret[0]->posted = extract_long(cret, 4);
1891 uret[0]->axlevel = extract_int(cret, 5);
1892 uret[0]->usernum = extract_long(cret, 6);
1893 uret[0]->lastcall = extract_long(cret, 7);
1894 uret[0]->USuserpurge = extract_int(cret, 8);
1901 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1906 if (!cret) return -2;
1907 if (!uret) return -2;
1909 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1910 if (!aaa) return -1;
1912 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1913 uret->fullname, uret->password, uret->flags, uret->timescalled,
1914 uret->posted, uret->axlevel, uret->usernum, uret->lastcall, uret->USuserpurge
1916 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1923 int CtdlIPCAideGetEmailAddresses(CtdlIPC *ipc, const char *who, char *target_buf, char *cret)
1927 char *emailaddrs = NULL;
1928 size_t emailaddrs_len = 0;
1930 sprintf(aaa, "AGEA %s", who);
1931 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
1933 if (ret / 100 == 1) {
1934 strcpy(target_buf, emailaddrs);
1937 if (emailaddrs != NULL) {
1946 int CtdlIPCAideSetEmailAddresses(CtdlIPC *ipc, const char *who, char *emailaddrs, char *cret)
1951 if (!who) return -2;
1952 if (!emailaddrs) return -2;
1953 if (!cret) return -2;
1955 sprintf(aaa, "ASEA %s", who);
1956 ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
1962 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1963 /* caller must free the struct ExpirePolicy */
1964 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
1965 struct ExpirePolicy **policy, char *cret)
1967 static char *proto[] = {
1971 strof(mailboxespolicy)
1976 if (!cret) return -2;
1977 if (!policy) return -2;
1978 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1979 if (!*policy) return -1;
1980 if (which < 0 || which > 3) return -2;
1982 sprintf(cmd, "GPEX %s", proto[which]);
1983 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1984 if (ret / 100 == 2) {
1985 policy[0]->expire_mode = extract_int(cret, 0);
1986 policy[0]->expire_value = extract_int(cret, 1);
1993 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1994 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1995 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1996 struct ExpirePolicy *policy, char *cret)
1999 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
2001 if (!cret) return -2;
2002 if (which < 0 || which > 3) return -2;
2003 if (!policy) return -2;
2004 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
2005 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
2007 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
2008 policy->expire_mode, policy->expire_value);
2009 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2014 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
2018 if (!cret) return -2;
2019 if (!listing) return -2;
2020 if (*listing) return -2;
2022 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
2023 listing, &bytes, cret);
2028 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
2030 if (!cret) return -2;
2031 if (!listing) return -2;
2033 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
2039 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype, char **listing, char *cret)
2045 if (!cret) return -2;
2046 if (!mimetype) return -2;
2047 if (!listing) return -2;
2048 if (*listing) return -2;
2050 aaa = malloc(strlen(mimetype) + 13);
2051 if (!aaa) return -1;
2052 sprintf(aaa, "CONF GETSYS|%s", mimetype);
2053 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
2060 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype, const char *listing, char *cret)
2065 if (!cret) return -2;
2066 if (!mimetype) return -2;
2067 if (!listing) return -2;
2069 aaa = malloc(strlen(mimetype) + 13);
2070 if (!aaa) return -1;
2071 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
2072 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing), NULL, NULL, cret);
2079 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
2083 if (!cret) return -2;
2084 if (!listing) return -2;
2085 if (*listing) return -2;
2087 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0, listing, &bytes, cret);
2092 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
2094 if (!cret) return -2;
2095 if (!listing) return -2;
2097 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing), NULL, NULL, cret);
2102 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2106 if (!cret) return -2;
2107 if (session < 0) return -2;
2109 sprintf(aaa, "REQT %d", session);
2110 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2115 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2119 if (!cret) return -2;
2120 if (msgnum < 0) return -2;
2122 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2123 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2128 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2137 /* New SSL object */
2138 temp_ssl = SSL_new(ssl_ctx);
2140 error_printf("SSL_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2143 /* Pointless flag waving */
2144 #if SSLEAY_VERSION_NUMBER >= 0x0922
2145 SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
2148 /* Associate network connection with SSL object */
2149 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2150 error_printf("SSL_set_fd failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2154 if (status_hook != NULL) {
2155 status_hook("Requesting encryption...\r");
2158 /* Ready to start SSL/TLS */
2159 r = CtdlIPCGenericCommand(ipc, "STLS", NULL, 0, NULL, NULL, cret);
2161 error_printf("Server can't start TLS: %s\n", buf);
2166 /* Do SSL/TLS handshake */
2167 if ((a = SSL_connect(temp_ssl)) < 1) {
2168 error_printf("SSL_connect failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2172 ipc->ssl = temp_ssl;
2174 error_printf("Encrypting with %s cipher %s\n",
2175 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2176 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
2181 #endif /* HAVE_OPENSSL */
2186 static void endtls(SSL *ssl)
2197 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2202 if (!address) return -2;
2203 if (!cret) return -2;
2205 aaa = (char *)malloc(strlen(address) + 6);
2206 if (!aaa) return -1;
2208 sprintf(aaa, "QDIR %s", address);
2209 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2216 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2220 if (!cret) return -2;
2221 sprintf(aaa, "IPGM %d", secret);
2222 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2228 /* ************************************************************************** */
2229 /* Stuff below this line is not for public consumption */
2230 /* ************************************************************************** */
2233 /* Read a listing from the server up to 000. Append to dest if it exists */
2234 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2243 length = strlen(ret);
2248 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2249 linelength = strlen(aaa);
2250 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2252 strcpy(&ret[length], aaa);
2253 length += linelength;
2254 strcpy(&ret[length++], "\n");
2262 /* Send a listing to the server; generate the ending 000. */
2263 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2267 text = (char *)malloc(strlen(listing) + 6);
2269 strcpy(text, listing);
2270 while (text[strlen(text) - 1] == '\n')
2271 text[strlen(text) - 1] = '\0';
2272 strcat(text, "\n000");
2273 CtdlIPC_putline(ipc, text);
2277 /* Malloc failed but we are committed to send */
2278 /* This may result in extra blanks at the bottom */
2279 CtdlIPC_putline(ipc, text);
2280 CtdlIPC_putline(ipc, "000");
2286 /* Partial read of file from server */
2287 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2293 if (!cret) return 0;
2294 if (bytes < 1) return 0;
2297 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2298 CtdlIPC_putline(ipc, aaa);
2299 CtdlIPC_getline(ipc, aaa);
2301 strcpy(cret, &aaa[4]);
2303 len = extract_long(&aaa[4], 0);
2304 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2306 /* I know what I'm doing */
2307 serv_read(ipc, ((char *)(*buf) + offset), len);
2309 /* We have to read regardless */
2310 serv_read(ipc, aaa, len);
2314 CtdlIPC_unlock(ipc);
2320 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2324 if (!cret) return -2;
2325 if (!ipc->downloading) return -2;
2327 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2329 ipc->downloading = 0;
2335 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2339 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2340 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2347 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2348 void (*progress_gauge_callback)
2349 (CtdlIPC*, unsigned long, unsigned long),
2354 if (!cret) return -1;
2355 if (!buf) return -1;
2356 if (*buf) return -1;
2357 if (!ipc->downloading) return -1;
2360 if (progress_gauge_callback)
2361 progress_gauge_callback(ipc, len, bytes);
2362 while (len < bytes) {
2365 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2371 if (progress_gauge_callback)
2372 progress_gauge_callback(ipc, len, bytes);
2377 /* READ - pipelined */
2378 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2380 void (*progress_gauge_callback)
2381 (CtdlIPC*, unsigned long, unsigned long),
2385 int calls; /* How many calls in the pipeline */
2386 int i; /* iterator */
2389 if (!cret) return -1;
2390 if (!buf) return -1;
2391 if (*buf) return -1;
2392 if (!ipc->downloading) return -1;
2394 *buf = (void *)realloc(*buf, bytes - resume);
2395 if (!*buf) return -1;
2399 if (progress_gauge_callback)
2400 progress_gauge_callback(ipc, len, bytes);
2402 /* How many calls will be in the pipeline? */
2403 calls = (bytes - resume) / 4096;
2404 if ((bytes - resume) % 4096) calls++;
2406 /* Send all requests at once */
2407 for (i = 0; i < calls; i++) {
2408 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2409 CtdlIPC_putline(ipc, aaa);
2412 /* Receive all responses at once */
2413 for (i = 0; i < calls; i++) {
2414 CtdlIPC_getline(ipc, aaa);
2416 strcpy(cret, &aaa[4]);
2418 len = extract_long(&aaa[4], 0);
2419 /* I know what I'm doing */
2420 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2422 if (progress_gauge_callback)
2423 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2425 CtdlIPC_unlock(ipc);
2431 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2436 if (!cret) return -1;
2437 if (!ipc->uploading) return -1;
2439 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2440 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2447 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2448 void (*progress_gauge_callback)
2449 (CtdlIPC*, unsigned long, unsigned long),
2457 FILE *fd = uploadFP;
2460 if (!cret) return -1;
2462 fseek(fd, 0L, SEEK_END);
2466 if (progress_gauge_callback)
2467 progress_gauge_callback(ipc, 0, bytes);
2469 while (offset < bytes) {
2472 /* Read some data in */
2473 to_write = fread(buf, 1, 4096, fd);
2475 if (feof(fd) || ferror(fd)) break;
2477 sprintf(aaa, "WRIT %d", (int)to_write);
2478 CtdlIPC_putline(ipc, aaa);
2479 CtdlIPC_getline(ipc, aaa);
2480 strcpy(cret, &aaa[4]);
2482 if (aaa[0] == '7') {
2483 to_write = extract_long(&aaa[4], 0);
2485 serv_write(ipc, buf, to_write);
2487 if (progress_gauge_callback)
2488 progress_gauge_callback(ipc, offset, bytes);
2489 /* Detect short reads and back up if needed */
2490 /* offset will never be negative anyway */
2491 fseek(fd, (signed)offset, SEEK_SET);
2496 if (progress_gauge_callback)
2497 progress_gauge_callback(ipc, 1, 1);
2500 return (!ferr ? ret : -2);
2505 * Generic command method. This method should handle any server command
2506 * except for CHAT. It takes the following arguments:
2508 * ipc The server to speak with
2509 * command Preformatted command to send to server
2510 * to_send A text or binary file to send to server
2511 * (only sent if server requests it)
2512 * bytes_to_send The number of bytes in to_send (required if
2513 * sending binary, optional if sending listing)
2514 * to_receive Pointer to a NULL pointer, if the server
2515 * sends text or binary we will allocate memory
2516 * for the file and stuff it here
2517 * bytes_to_receive If a file is received, we will store its
2519 * proto_response The protocol response. Caller must provide
2520 * this buffer and ensure that it is at least
2521 * 128 bytes in length.
2523 * This function returns a number equal to the protocol response number,
2524 * -1 if an internal error occurred, -2 if caller provided bad values,
2525 * or 0 - the protocol response number if bad values were found during
2526 * the protocol exchange.
2527 * It stores the protocol response string (minus the number) in
2528 * protocol_response as described above. Some commands send additional
2529 * data in this string.
2531 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2532 const char *command, const char *to_send,
2533 size_t bytes_to_send, char **to_receive,
2534 size_t *bytes_to_receive, char *proto_response)
2539 if (!command) return -2;
2540 if (!proto_response) return -2;
2543 CtdlIPC_putline(ipc, command);
2545 CtdlIPC_getline(ipc, proto_response);
2546 if (proto_response[3] == '*')
2548 ret = atoi(proto_response);
2549 strcpy(proto_response, &proto_response[4]);
2550 switch (ret / 100) {
2551 default: /* Unknown, punt */
2553 case 3: /* MORE_DATA */
2555 /* Don't need to do anything */
2557 case 1: /* LISTING_FOLLOWS */
2558 if (to_receive && !*to_receive && bytes_to_receive) {
2559 *to_receive = CtdlIPCReadListing(ipc, NULL);
2560 } else { /* Drain */
2561 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2565 case 4: /* SEND_LISTING */
2567 CtdlIPCSendListing(ipc, to_send);
2569 /* No listing given, fake it */
2570 CtdlIPC_putline(ipc, "000");
2574 case 6: /* BINARY_FOLLOWS */
2575 if (to_receive && !*to_receive && bytes_to_receive) {
2577 extract_long(proto_response, 0);
2578 *to_receive = (char *)
2579 malloc((size_t)*bytes_to_receive);
2583 serv_read(ipc, *to_receive,
2590 drain = extract_long(proto_response, 0);
2591 while (drain > SIZ) {
2592 serv_read(ipc, buf, SIZ);
2595 serv_read(ipc, buf, drain);
2599 case 7: /* SEND_BINARY */
2600 if (to_send && bytes_to_send) {
2601 serv_write(ipc, to_send, bytes_to_send);
2602 } else if (bytes_to_send) {
2603 /* Fake it, send nulls */
2606 fake = bytes_to_send;
2607 memset(buf, '\0', SIZ);
2608 while (fake > SIZ) {
2609 serv_write(ipc, buf, SIZ);
2612 serv_write(ipc, buf, fake);
2614 } /* else who knows? DANGER WILL ROBINSON */
2616 case 8: /* START_CHAT_MODE */
2617 if (!strncasecmp(command, "CHAT", 4)) {
2618 /* Don't call chatmode with generic! */
2619 CtdlIPC_putline(ipc, "/quit");
2622 /* In this mode we send then receive listing */
2624 CtdlIPCSendListing(ipc, to_send);
2626 /* No listing given, fake it */
2627 CtdlIPC_putline(ipc, "000");
2630 if (to_receive && !*to_receive
2631 && bytes_to_receive) {
2632 *to_receive = CtdlIPCReadListing(ipc, NULL);
2633 } else { /* Drain */
2634 while (CtdlIPC_getline(ipc, buf),
2635 strcmp(buf, "000")) ;
2640 case 9: /* ASYNC_MSG */
2641 /* CtdlIPCDoAsync(ret, proto_response); */
2642 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2648 CtdlIPC_unlock(ipc);
2654 * Connect to a Citadel on a remote host using a TCP/IP socket
2656 static int tcp_connectsock(char *host, char *service)
2658 struct in6_addr serveraddr;
2659 struct addrinfo hints;
2660 struct addrinfo *res = NULL;
2661 struct addrinfo *ai = NULL;
2665 if ((host == NULL) || IsEmptyStr(host)) {
2666 service = DEFAULT_HOST ;
2668 if ((service == NULL) || IsEmptyStr(service)) {
2669 service = DEFAULT_PORT ;
2672 memset(&hints, 0x00, sizeof(hints));
2673 hints.ai_flags = AI_NUMERICSERV;
2674 hints.ai_family = AF_UNSPEC;
2675 hints.ai_socktype = SOCK_STREAM;
2678 * Handle numeric IPv4 and IPv6 addresses
2680 rc = inet_pton(AF_INET, host, &serveraddr);
2681 if (rc == 1) { /* dotted quad */
2682 hints.ai_family = AF_INET;
2683 hints.ai_flags |= AI_NUMERICHOST;
2686 rc = inet_pton(AF_INET6, host, &serveraddr);
2687 if (rc == 1) { /* IPv6 address */
2688 hints.ai_family = AF_INET6;
2689 hints.ai_flags |= AI_NUMERICHOST;
2693 /* Begin the connection process */
2695 rc = getaddrinfo(host, service, &hints, &res);
2701 * Try all available addresses until we connect to one or until we run out.
2703 for (ai = res; ai != NULL; ai = ai->ai_next) {
2704 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2705 if (sock < 0) return(-1);
2707 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2709 return(sock); /* Connected! */
2712 close(sock); /* Failed. Close the socket to avoid fd leak! */
2724 * Connect to a Citadel on the local host using a unix domain socket
2726 static int uds_connectsock(int *isLocal, char *sockpath)
2728 struct sockaddr_un addr;
2731 memset(&addr, 0, sizeof(addr));
2732 addr.sun_family = AF_UNIX;
2733 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2735 s = socket(AF_UNIX, SOCK_STREAM, 0);
2740 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2751 * input binary data from socket
2753 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2755 unsigned int len, rlen;
2757 #if defined(HAVE_OPENSSL)
2759 serv_read_ssl(ipc, buf, bytes);
2764 while (len < bytes) {
2765 rlen = read(ipc->sock, &buf[len], bytes - len);
2767 connection_died(ipc, 0);
2776 * send binary to server
2778 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2780 unsigned int bytes_written = 0;
2783 #if defined(HAVE_OPENSSL)
2785 serv_write_ssl(ipc, buf, nbytes);
2789 while (bytes_written < nbytes) {
2790 retval = write(ipc->sock, &buf[bytes_written],
2791 nbytes - bytes_written);
2793 connection_died(ipc, 0);
2796 bytes_written += retval;
2803 * input binary data from encrypted connection
2805 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2811 while (len < bytes) {
2812 if (SSL_want_read(ipc->ssl)) {
2813 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2814 error_printf("SSL_write in serv_read:\n");
2815 ERR_print_errors_fp(stderr);
2818 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2822 errval = SSL_get_error(ipc->ssl, rlen);
2823 if (errval == SSL_ERROR_WANT_READ ||
2824 errval == SSL_ERROR_WANT_WRITE) {
2829 Not sure why we'd want to handle these error codes any differently,
2830 but this definitely isn't the way to handle them. Someone must have
2831 naively assumed that we could fall back to unencrypted communications,
2832 but all it does is just recursively blow the stack.
2833 if (errval == SSL_ERROR_ZERO_RETURN ||
2834 errval == SSL_ERROR_SSL) {
2835 serv_read(ipc, &buf[len], bytes - len);
2839 error_printf("SSL_read in serv_read: %s\n",
2840 ERR_reason_error_string(ERR_peek_error()));
2841 connection_died(ipc, 1);
2850 * send binary to server encrypted
2852 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2854 unsigned int bytes_written = 0;
2858 while (bytes_written < nbytes) {
2859 if (SSL_want_write(ipc->ssl)) {
2860 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2861 error_printf("SSL_read in serv_write:\n");
2862 ERR_print_errors_fp(stderr);
2865 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2866 nbytes - bytes_written);
2870 errval = SSL_get_error(ipc->ssl, retval);
2871 if (errval == SSL_ERROR_WANT_READ ||
2872 errval == SSL_ERROR_WANT_WRITE) {
2876 if (errval == SSL_ERROR_ZERO_RETURN ||
2877 errval == SSL_ERROR_SSL) {
2878 serv_write(ipc, &buf[bytes_written],
2879 nbytes - bytes_written);
2882 error_printf("SSL_write in serv_write: %s\n",
2883 ERR_reason_error_string(ERR_peek_error()));
2884 connection_died(ipc, 1);
2887 bytes_written += retval;
2894 static void CtdlIPC_init_OpenSSL(void)
2897 const SSL_METHOD *ssl_method;
2900 /* already done init */
2909 SSL_load_error_strings();
2910 SSLeay_add_ssl_algorithms();
2912 /* Set up the SSL context in which we will oeprate */
2913 ssl_method = SSLv23_client_method();
2914 ssl_ctx = SSL_CTX_new(ssl_method);
2916 error_printf("SSL_CTX_new failed: %s\n", ERR_reason_error_string(ERR_get_error()));
2919 /* Any reasonable cipher we can get */
2920 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2921 error_printf("No ciphers available for encryption\n");
2924 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2926 /* Load DH parameters into the context */
2929 error_printf("Can't allocate a DH object: %s\n", ERR_reason_error_string(ERR_get_error()));
2933 if (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
2934 error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
2939 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2943 #endif /* HAVE_OPENSSL */
2947 ReadNetworkChunk(CtdlIPC* ipc)
2963 FD_SET(ipc->sock, &read_fd);
2964 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2968 *(ipc->BufPtr) = '\0';
2969 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2971 ipc->BufPtr[n]='\0';
2979 if (!(errno == EINTR || errno == EAGAIN))
2980 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2986 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2988 ipc->BufPtr[n]='\0';
2993 connection_died(ipc, 0);
3001 * input string from socket - implemented in terms of serv_read()
3005 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3008 char *aptr, *bptr, *aeptr, *beptr;
3010 // error_printf("---\n");
3013 #if defined(HAVE_OPENSSL)
3016 /* Read one character at a time. */
3018 serv_read(ipc, &buf[i], 1);
3019 if (buf[i] == '\n' || i == (SIZ-1))
3023 /* If we got a long line, discard characters until the newline. */
3025 while (buf[i] != '\n')
3026 serv_read(ipc, &buf[i], 1);
3028 /* Strip the trailing newline (and carriage return, if present) */
3029 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3030 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3035 if (ipc->Buf == NULL)
3038 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3040 ipc->BufPtr = ipc->Buf;
3044 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3045 if (ipc->BufUsed == 0)
3046 ReadNetworkChunk(ipc);
3048 //// if (ipc->BufUsed != 0) while (1)
3054 aeptr = ipc->Buf + ipc->BufSize;
3055 while ((aptr < aeptr) &&
3059 *(bptr++) = *(aptr++);
3060 if ((*aptr == '\n') && (aptr < aeptr))
3062 /* Terminate it right, remove the line breaks */
3063 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3065 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3068 // 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);
3069 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3072 /* is there more in the buffer we need to read later? */
3073 if (ipc->Buf + ipc->BufUsed > aptr)
3080 ipc->BufPtr = ipc->Buf;
3082 // error_printf("----bla6\n");
3085 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3086 else if ((ipc->BufPtr != ipc->Buf) &&
3087 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3089 size_t NewBufSize = ipc->BufSize * 2;
3090 int delta = (ipc->BufPtr - ipc->Buf);
3093 /* if the line would end after our buffer, we should use a bigger buffer. */
3094 NewBuf = (char *)malloc (NewBufSize + 10);
3095 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3097 ipc->Buf = ipc->BufPtr = NewBuf;
3098 ipc->BufUsed -= delta;
3099 ipc->BufSize = NewBufSize;
3101 if (ReadNetworkChunk(ipc) <0)
3103 // error_printf("----bla\n");
3107 /// error_printf("----bl45761%s\nipc->BufUsed");
3109 // error_printf("----bla1\n");
3112 #else /* CHUNKED_READ */
3114 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3118 /* Read one character at a time. */
3120 serv_read(ipc, &buf[i], 1);
3121 if (buf[i] == '\n' || i == (SIZ-1))
3125 /* If we got a long line, discard characters until the newline. */
3127 while (buf[i] != '\n')
3128 serv_read(ipc, &buf[i], 1);
3130 /* Strip the trailing newline (and carriage return, if present) */
3131 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3132 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3136 #endif /* CHUNKED_READ */
3139 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3141 CtdlIPC_getline(ipc, buf);
3145 * send line to server - implemented in terms of serv_write()
3147 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3153 cmd = malloc(len + 2);
3155 /* This requires no extra memory */
3156 serv_write(ipc, buf, len);
3157 serv_write(ipc, "\n", 1);
3159 /* This is network-optimized */
3160 strncpy(cmd, buf, len);
3161 strcpy(cmd + len, "\n");
3162 serv_write(ipc, cmd, len + 1);
3166 ipc->last_command_sent = time(NULL);
3169 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3171 CtdlIPC_putline(ipc, buf);
3178 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3186 ipc = malloc(sizeof(struct _CtdlIPC));
3190 #if defined(HAVE_OPENSSL)
3192 CtdlIPC_init_OpenSSL();
3194 ipc->sock = -1; /* Not connected */
3195 ipc->isLocal = 0; /* Not local, of course! */
3196 ipc->downloading = 0;
3198 ipc->last_command_sent = 0L;
3199 ipc->network_status_cb = NULL;
3204 strcpy(cithost, DEFAULT_HOST); /* default host */
3205 strcpy(citport, DEFAULT_PORT); /* default port */
3207 /* Allow caller to supply our values */
3208 if (hostbuf && strlen(hostbuf) > 0) {
3209 strcpy(cithost, hostbuf);
3211 if (portbuf && strlen(portbuf) > 0) {
3212 strcpy(citport, portbuf);
3215 /* Read host/port from command line if present */
3216 for (a = 0; a < argc; ++a) {
3219 } else if (a == 1) {
3220 strcpy(cithost, argv[a]);
3221 } else if (a == 2) {
3222 strcpy(citport, argv[a]);
3224 error_printf("%s: usage: ",argv[0]);
3225 error_printf("%s [host] [port] ",argv[0]);
3232 if ((!strcmp(cithost, "localhost")) || (!strcmp(cithost, "127.0.0.1"))) {
3236 /* If we're using a unix domain socket we can do a bunch of stuff */
3237 if (!strcmp(cithost, UDS)) {
3238 if (!strcasecmp(citport, DEFAULT_PORT)) {
3239 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3242 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3244 printf("[%s]\n", sockpath);
3245 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3246 if (ipc->sock == -1) {
3250 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3251 if (portbuf != NULL) strcpy(portbuf, sockpath);
3252 strcpy(ipc->ip_hostname, "");
3253 strcpy(ipc->ip_address, "");
3257 printf("[%s:%s]\n", cithost, citport);
3258 ipc->sock = tcp_connectsock(cithost, citport);
3259 if (ipc->sock == -1) {
3265 /* Learn the actual network identity of the host to which we are connected */
3267 struct sockaddr_in6 clientaddr;
3268 unsigned int addrlen = sizeof(clientaddr);
3270 ipc->ip_hostname[0] = 0;
3271 ipc->ip_address[0] = 0;
3273 getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3274 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3275 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3277 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3278 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3281 /* stuff other things elsewhere */
3283 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3284 if (portbuf != NULL) strcpy(portbuf, citport);
3290 * Disconnect and delete the IPC class (destructor)
3292 void CtdlIPC_delete(CtdlIPC* ipc)
3296 SSL_shutdown(ipc->ssl);
3301 if (ipc->sock > -1) {
3302 shutdown(ipc->sock, 2); /* Close it up */
3305 if (ipc->Buf != NULL)
3314 * Disconnect and delete the IPC class (destructor)
3315 * Also NULLs out the pointer
3317 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3319 CtdlIPC_delete(*pipc);
3325 * return the file descriptor of the server socket so we can select() on it.
3327 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3330 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3337 * return one character
3339 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3342 char CtdlIPC_get(CtdlIPC* ipc)
3347 serv_read(ipc, buf, 1);