16 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <netinet/in.h>
29 #ifdef THREADED_CLIENT
33 #include "citadel_ipc.h"
34 #include "citadel_decls.h"
36 #include "citadel_dirs.h"
37 #ifdef THREADED_CLIENT
38 pthread_mutex_t rwlock;
42 static SSL_CTX *ssl_ctx;
45 #ifdef THREADED_CLIENT
46 pthread_mutex_t **Critters; /* Things that need locking */
47 #endif /* THREADED_CLIENT */
49 #endif /* HAVE_OPENSSL */
52 #define INADDR_NONE 0xffffffff
55 static void (*status_hook)(char *s) = NULL;
57 void setCryptoStatusHook(void (*hook)(char *s)) {
61 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
62 ipc->network_status_cb = hook;
66 char instant_msgs = 0;
69 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
70 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
72 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
73 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
74 static void ssl_lock(int mode, int n, const char *file, int line);
75 static void endtls(SSL *ssl);
76 #ifdef THREADED_CLIENT
77 static unsigned long id_callback(void);
78 #endif /* THREADED_CLIENT */
79 #endif /* HAVE_OPENSSL */
80 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
81 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
85 * Does nothing. The server should always return 200.
87 int CtdlIPCNoop(CtdlIPC *ipc)
91 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
96 * Does nothing interesting. The server should always return 200
97 * along with your string.
99 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
105 if (!cret) return -2;
107 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
110 sprintf(aaa, "ECHO %s", arg);
111 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
118 * Asks the server to close the connecction.
119 * Should always return 200.
121 int CtdlIPCQuit(CtdlIPC *ipc)
123 register int ret = 221; /* Default to successful quit */
127 if (ipc->sock > -1) {
128 CtdlIPC_putline(ipc, "QUIT");
129 CtdlIPC_getline(ipc, aaa);
134 SSL_shutdown(ipc->ssl);
138 shutdown(ipc->sock, 2); /* Close connection; we're dead */
146 * Asks the server to logout. Should always return 200, even if no user
147 * was logged in. The user will not be logged in after this!
149 int CtdlIPCLogout(CtdlIPC *ipc)
155 CtdlIPC_putline(ipc, "LOUT");
156 CtdlIPC_getline(ipc, aaa);
164 * First stage of authentication - pass the username. Returns 300 if the
165 * username is able to log in, with the username correctly spelled in cret.
166 * Returns various 500 error codes if the user doesn't exist, etc.
168 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
173 if (!username) return -2;
174 if (!cret) return -2;
176 aaa = (char *)malloc((size_t)(strlen(username) + 6));
179 sprintf(aaa, "USER %s", username);
180 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
187 * Second stage of authentication - provide password. The server returns
188 * 200 and several arguments in cret relating to the user's account.
190 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
195 if (!passwd) return -2;
196 if (!cret) return -2;
198 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
201 sprintf(aaa, "PASS %s", passwd);
202 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
209 * Second stage of authentication - provide password. The server returns
210 * 200 and several arguments in cret relating to the user's account.
212 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
217 if (!response) return -2;
218 if (!cret) return -2;
220 aaa = (char *)malloc((size_t)(strlen(response) + 6));
223 sprintf(aaa, "PAS2 %s", response);
224 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
231 * Create a new user. This returns 200 plus the same arguments as TryPassword
232 * if selfservice is nonzero, unless there was a problem creating the account.
233 * If selfservice is zero, creates a new user but does not log out the existing
234 * user - intended for use by system administrators to create accounts on
235 * behalf of other users.
237 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
242 if (!username) return -2;
243 if (!cret) return -2;
245 aaa = (char *)malloc((size_t)(strlen(username) + 6));
248 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
249 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
256 * Changes the user's password. Returns 200 if changed, errors otherwise.
258 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
263 if (!passwd) return -2;
264 if (!cret) return -2;
266 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
269 sprintf(aaa, "SETP %s", passwd);
270 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
277 /* Caller must free the march list */
278 /* Room types are defined in enum RoomList; keep these in sync! */
279 /* floor is -1 for all, or floornum */
280 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
283 struct march *march = NULL;
284 static char *proto[] =
285 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
290 if (!listing) return -2;
291 if (*listing) return -2; /* Free the listing first */
292 if (!cret) return -2;
293 /* if (which < 0 || which > 4) return -2; */
294 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
296 sprintf(aaa, "%s %d", proto[which], floor);
297 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
298 if (ret / 100 == 1) {
301 while (bbb && strlen(bbb)) {
304 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
306 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
307 mptr = (struct march *) malloc(sizeof (struct march));
310 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
311 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
312 mptr->march_floor = (char) extract_int(aaa, 2);
313 mptr->march_order = (char) extract_int(aaa, 3);
314 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
315 mptr->march_access = (char) extract_int(aaa, 5);
322 while (mptr2->next != NULL)
336 /* Caller must free the struct ctdluser; caller may pass an existing one */
337 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
341 if (!cret) return -2;
342 if (!uret) return -2;
343 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
344 if (!*uret) return -1;
346 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
347 if (ret / 100 == 2) {
348 uret[0]->USscreenwidth = extract_int(cret, 0);
349 uret[0]->USscreenheight = extract_int(cret, 1);
350 uret[0]->flags = extract_int(cret, 2);
357 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
361 if (!uret) return -2;
362 if (!cret) return -2;
364 sprintf(aaa, "SETU %d|%d|%d",
365 uret->USscreenwidth, uret->USscreenheight,
367 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
372 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
373 struct ctdlipcroom **rret, char *cret)
378 if (!cret) return -2;
379 if (!rret) return -2;
380 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
381 if (!*rret) return -1;
384 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
389 sprintf(aaa, "GOTO %s|%s", room, passwd);
391 aaa = (char *)malloc(strlen(room) + 6);
396 sprintf(aaa, "GOTO %s", room);
398 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
399 if (ret / 100 == 2) {
400 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
401 rret[0]->RRunread = extract_long(cret, 1);
402 rret[0]->RRtotal = extract_long(cret, 2);
403 rret[0]->RRinfoupdated = extract_int(cret, 3);
404 rret[0]->RRflags = extract_int(cret, 4);
405 rret[0]->RRhighest = extract_long(cret, 5);
406 rret[0]->RRlastread = extract_long(cret, 6);
407 rret[0]->RRismailbox = extract_int(cret, 7);
408 rret[0]->RRaide = extract_int(cret, 8);
409 rret[0]->RRnewmail = extract_long(cret, 9);
410 rret[0]->RRfloor = extract_int(cret, 10);
411 rret[0]->RRflags2 = extract_int(cret, 14);
422 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
423 /* whicharg is number of messages, applies to last, first, gt, lt */
424 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
425 const char *mtemplate, unsigned long **mret, char *cret)
428 register unsigned long count = 0;
429 static char *proto[] =
430 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
435 if (!cret) return -2;
436 if (!mret) return -2;
437 if (*mret) return -2;
438 if (which < 0 || which > 6) return -2;
441 sprintf(aaa, "MSGS %s||%d", proto[which],
442 (mtemplate) ? 1 : 0);
444 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
445 (mtemplate) ? 1 : 0);
446 if (mtemplate) count = strlen(mtemplate);
447 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
451 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
454 while (bbb && strlen(bbb)) {
455 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
456 remove_token(bbb, 0, '\n');
457 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
458 sizeof (unsigned long)));
460 (*mret)[count++] = atol(aaa);
472 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
473 struct ctdlipcmessage **mret, char *cret)
479 int multipart_hunting = 0;
480 char multipart_prefix[128];
482 if (!cret) return -1;
483 if (!mret) return -1;
484 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
485 if (!*mret) return -1;
486 if (!msgnum) return -1;
488 strcpy(mret[0]->content_type, "");
489 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
490 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
491 if (ret / 100 == 1) {
493 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
494 while (strlen(bbb) > 4 && bbb[4] == '=') {
495 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
496 remove_token(bbb, 0, '\n');
498 if (!strncasecmp(aaa, "nhdr=yes", 8))
500 else if (!strncasecmp(aaa, "from=", 5))
501 safestrncpy(mret[0]->author, &aaa[5], SIZ);
502 else if (!strncasecmp(aaa, "type=", 5))
503 mret[0]->type = atoi(&aaa[5]);
504 else if (!strncasecmp(aaa, "msgn=", 5))
505 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
506 else if (!strncasecmp(aaa, "subj=", 5))
507 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
508 else if (!strncasecmp(aaa, "rfca=", 5))
509 safestrncpy(mret[0]->email, &aaa[5], SIZ);
510 else if (!strncasecmp(aaa, "hnod=", 5))
511 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
512 else if (!strncasecmp(aaa, "room=", 5))
513 safestrncpy(mret[0]->room, &aaa[5], SIZ);
514 else if (!strncasecmp(aaa, "node=", 5))
515 safestrncpy(mret[0]->node, &aaa[5], SIZ);
516 else if (!strncasecmp(aaa, "rcpt=", 5))
517 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
518 else if (!strncasecmp(aaa, "time=", 5))
519 mret[0]->time = atol(&aaa[5]);
521 /* Multipart/alternative prefix & suffix strings help
522 * us to determine which part we want to download.
524 else if (!strncasecmp(aaa, "pref=", 5)) {
525 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
526 if (!strcasecmp(multipart_prefix,
527 "multipart/alternative")) {
531 else if (!strncasecmp(aaa, "suff=", 5)) {
532 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
533 if (!strcasecmp(multipart_prefix,
534 "multipart/alternative")) {
539 else if (!strncasecmp(aaa, "part=", 5)) {
540 struct parts *ptr, *chain;
542 ptr = (struct parts *)calloc(1, sizeof (struct parts));
545 /* Fill the buffers for the caller */
546 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
547 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
548 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
549 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
550 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
551 ptr->length = extract_long(&aaa[5], 5);
552 if (!mret[0]->attachments)
553 mret[0]->attachments = ptr;
555 chain = mret[0]->attachments;
561 /* Now handle multipart/alternative */
562 if (multipart_hunting > 0) {
563 if ( (!strcasecmp(ptr->mimetype,
565 || (!strcasecmp(ptr->mimetype,
567 strcpy(mret[0]->mime_chosen,
575 /* Eliminate "text\n" */
576 remove_token(bbb, 0, '\n');
578 /* If doing a MIME thing, pull out the extra headers */
581 if (!strncasecmp(bbb, "Content-type:", 13)) {
582 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
583 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
584 striplt(mret[0]->content_type);
586 /* strip out ";charset=" portion. FIXME do something with
587 * the charset (like... convert it) instead of just throwing
590 if (strstr(mret[0]->content_type, ";") != NULL) {
591 strcpy(strstr(mret[0]->content_type, ";"), "");
595 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
596 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
597 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
598 striplt(mret[0]->mime_chosen);
600 remove_token(bbb, 0, '\n');
601 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
602 remove_token(bbb, 0, '\n');
608 /* FIXME: Strip trailing whitespace */
609 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
611 bbb = (char *)realloc(bbb, 1);
621 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
626 if (!cret) return -2;
627 if (!listing) return -2;
628 if (*listing) return -2;
630 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
636 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
640 char *listing = NULL;
643 if (!cret) return -2;
645 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
646 if (ret / 100 == 1) {
649 while (*listing && strlen(listing)) {
650 extract_token(buf, listing, 0, '\n', sizeof buf);
651 remove_token(listing, 0, '\n');
653 case 0: ipc->ServInfo.pid = atoi(buf);
655 case 1: strcpy(ipc->ServInfo.nodename,buf);
657 case 2: strcpy(ipc->ServInfo.humannode,buf);
659 case 3: strcpy(ipc->ServInfo.fqdn,buf);
661 case 4: strcpy(ipc->ServInfo.software,buf);
663 case 5: ipc->ServInfo.rev_level = atoi(buf);
665 case 6: strcpy(ipc->ServInfo.site_location,buf);
667 case 7: strcpy(ipc->ServInfo.sysadm,buf);
669 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
671 case 10: ipc->ServInfo.ok_floors = atoi(buf);
673 case 11: ipc->ServInfo.paging_level = atoi(buf);
675 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
677 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
679 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
685 if (listing) free(listing);
691 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
696 if (!cret) return -2;
697 if (!listing) return -2;
698 if (*listing) return -2;
700 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
706 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
708 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
713 if (!cret) return -2;
716 sprintf(aaa, "SLRP %ld", msgnum);
719 sprintf(aaa, "SLRP HIGHEST");
721 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
727 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
732 if (!cret) return -2;
733 if (!username) return -2;
735 aaa = (char *)malloc(strlen(username) + 6);
738 sprintf(aaa, "INVT %s", username);
739 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
746 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
751 if (!cret) return -1;
752 if (!username) return -1;
754 aaa = (char *)malloc(strlen(username) + 6);
756 sprintf(aaa, "KICK %s", username);
757 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
764 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
768 if (!cret) return -2;
769 if (!qret) return -2;
770 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
771 if (!*qret) return -1;
773 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
774 if (ret / 100 == 2) {
775 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
776 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
777 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
778 qret[0]->QRflags = extract_int(cret, 3);
779 qret[0]->QRfloor = extract_int(cret, 4);
780 qret[0]->QRorder = extract_int(cret, 5);
781 qret[0]->QRdefaultview = extract_int(cret, 6);
782 qret[0]->QRflags2 = extract_int(cret, 7);
789 /* set forget to kick all users out of room */
790 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
795 if (!cret) return -2;
796 if (!qret) return -2;
798 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
799 strlen(qret->QRdirname) + 64);
802 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
803 qret->QRname, qret->QRpasswd, qret->QRdirname,
804 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
805 qret->QRdefaultview, qret->QRflags2);
806 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
813 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
815 if (!cret) return -1;
817 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
822 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
827 if (!cret) return -2;
828 if (!username) return -2;
830 aaa = (char *)malloc(strlen(username) + 6);
833 sprintf(aaa, "SETA %s", username);
834 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
841 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, const struct ctdlipcmessage *mr, char *cret)
846 if (!cret) return -2;
849 snprintf(cmd, sizeof cmd,
850 "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
851 mr->anonymous, mr->type, mr->subject, mr->author);
852 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
854 if ((flag == 0) && (subject_required != NULL)) {
855 /* Is the server strongly recommending that the user enter a message subject? */
856 if ((cret[3] != '\0') && (cret[4] != '\0')) {
857 *subject_required = extract_int(&cret[4], 1);
867 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
871 if (!cret) return -2;
872 if (!iret) return -2;
873 if (*iret) return -2;
875 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
880 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
884 if (!cret) return -2;
885 if (!msgnum) return -2;
887 sprintf(aaa, "DELE %ld", msgnum);
888 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
893 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
898 if (!cret) return -2;
899 if (!destroom) return -2;
900 if (!msgnum) return -2;
902 aaa = (char *)malloc(strlen(destroom) + 28);
905 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
906 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
913 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
917 if (!cret) return -2;
919 sprintf(aaa, "KILL %d", for_real);
920 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
925 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
926 const char *password, int floor, char *cret)
931 if (!cret) return -2;
932 if (!roomname) return -2;
935 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
937 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
940 aaa = (char *)malloc(strlen(roomname) + 40);
942 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
945 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
952 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
954 if (!cret) return -2;
956 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
961 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
967 if (!cret) return -2;
968 if (!mret) return -2;
969 if (*mret) return -2;
970 if (!message) return -2;
972 aaa = (char *)malloc(strlen(message) + 6);
975 sprintf(aaa, "MESG %s", message);
976 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
983 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
985 if (!cret) return -2;
987 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
992 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
998 if (!cret) return -2;
999 if (!rret) return -2;
1000 if (*rret) return -2;
1003 aaa = (char *)malloc(strlen(username) + 6);
1005 aaa = (char *)malloc(12);
1006 if (!aaa) return -1;
1009 sprintf(aaa, "GREG %s", username);
1011 sprintf(aaa, "GREG _SELF_");
1012 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1019 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1024 if (!cret) return -2;
1025 if (!username) return -2;
1026 if (axlevel < 0 || axlevel > 7) return -2;
1028 aaa = (char *)malloc(strlen(username) + 17);
1029 if (!aaa) return -1;
1031 sprintf(aaa, "VALI %s|%d", username, axlevel);
1032 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1039 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1043 if (!cret) return -1;
1044 if (!info) return -1;
1046 sprintf(aaa, "EINF %d", for_real);
1047 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1052 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1058 if (!cret) return -1;
1059 if (!listing) return -1;
1060 if (*listing) return -1;
1061 if (!searchstring) return -1;
1063 cmd = malloc(strlen(searchstring) + 10);
1064 sprintf(cmd, "LIST %s", searchstring);
1066 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1073 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1075 if (!cret) return -1;
1076 if (!info) return -1;
1078 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1084 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1088 if (!cret) return -1;
1089 if (!chek) return -1;
1091 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1092 if (ret / 100 == 2) {
1093 chek->newmail = extract_long(cret, 0);
1094 chek->needregis = extract_int(cret, 1);
1095 chek->needvalid = extract_int(cret, 2);
1102 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1107 if (!cret) return -2;
1108 if (!filename) return -2;
1110 aaa = (char *)malloc(strlen(filename) + 6);
1111 if (!aaa) return -1;
1113 sprintf(aaa, "DELF %s", filename);
1114 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1121 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1126 if (!cret) return -2;
1127 if (!filename) return -2;
1128 if (!destroom) return -2;
1130 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1131 if (!aaa) return -1;
1133 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1134 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1141 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1146 if (!cret) return -2;
1147 if (!filename) return -2;
1148 if (!destnode) return -2;
1150 aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1151 if (!aaa) return -1;
1153 sprintf(aaa, "NETF %s|%s", filename, destnode);
1154 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1161 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1166 if (!cret) return -1;
1167 if (!listing) return -1;
1168 if (*listing) return -1;
1170 *stamp = CtdlIPCServerTime(ipc, cret);
1172 *stamp = time(NULL);
1173 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1179 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1181 void (*progress_gauge_callback)
1182 (CtdlIPC*, unsigned long, unsigned long),
1191 if (!cret) return -2;
1192 if (!filename) return -2;
1193 if (!buf) return -2;
1194 if (*buf) return -2;
1195 if (ipc->downloading) return -2;
1197 aaa = (char *)malloc(strlen(filename) + 6);
1198 if (!aaa) return -1;
1200 sprintf(aaa, "OPEN %s", filename);
1201 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1203 if (ret / 100 == 2) {
1204 ipc->downloading = 1;
1205 bytes = extract_long(cret, 0);
1206 last_mod = extract_int(cret, 1);
1207 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1209 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1210 progress_gauge_callback, cret);
1212 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1213 progress_gauge_callback, cret);
1216 ret = CtdlIPCEndDownload(ipc, cret);
1218 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1219 filename, mimetype);
1226 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1228 void (*progress_gauge_callback)
1229 (CtdlIPC*, unsigned long, unsigned long),
1239 if (!cret) return -2;
1240 if (!buf) return -2;
1241 if (*buf) return -2;
1242 if (!part) return -2;
1243 if (!msgnum) return -2;
1244 if (ipc->downloading) return -2;
1246 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1247 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1248 if (ret / 100 == 2) {
1249 ipc->downloading = 1;
1250 bytes = extract_long(cret, 0);
1251 last_mod = extract_int(cret, 1);
1252 extract_token(filename, cret, 2, '|', sizeof filename);
1253 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1254 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1255 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1256 ret = CtdlIPCEndDownload(ipc, cret);
1258 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1259 filename, mimetype);
1266 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1267 void (*progress_gauge_callback)
1268 (CtdlIPC*, unsigned long, unsigned long),
1277 if (!cret) return -1;
1278 if (!buf) return -1;
1279 if (*buf) return -1;
1280 if (!filename) return -1;
1281 if (ipc->downloading) return -1;
1283 aaa = (char *)malloc(strlen(filename) + 6);
1284 if (!aaa) return -1;
1286 sprintf(aaa, "OIMG %s", filename);
1287 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1289 if (ret / 100 == 2) {
1290 ipc->downloading = 1;
1291 bytes = extract_long(cret, 0);
1292 last_mod = extract_int(cret, 1);
1293 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1294 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1295 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1296 ret = CtdlIPCEndDownload(ipc, cret);
1298 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1299 filename, mimetype);
1306 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1308 void (*progress_gauge_callback)
1309 (CtdlIPC*, unsigned long, unsigned long),
1315 if (!cret) return -1;
1316 if (!save_as) return -1;
1317 if (!comment) return -1;
1318 if (!path) return -1;
1319 if (!*path) return -1;
1320 if (ipc->uploading) return -1;
1322 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1323 if (!aaa) return -1;
1325 sprintf(aaa, "UOPN %s|%s", save_as, comment);
1326 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1328 if (ret / 100 == 2) {
1330 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1331 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1339 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1340 const char *save_as,
1341 void (*progress_gauge_callback)
1342 (CtdlIPC*, unsigned long, unsigned long),
1348 if (!cret) return -1;
1349 if (!save_as) return -1;
1350 if (!path && for_real) return -1;
1351 if (!*path && for_real) return -1;
1352 if (ipc->uploading) return -1;
1354 aaa = (char *)malloc(strlen(save_as) + 17);
1355 if (!aaa) return -1;
1357 sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1358 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1360 if (ret / 100 == 2 && for_real) {
1362 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1363 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1371 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1376 if (!cret) return -2;
1377 if (!username) return -2;
1379 aaa = (char *)malloc(strlen(username) + 6);
1380 if (!aaa) return -1;
1382 sprintf(aaa, "QUSR %s", username);
1383 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1390 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1394 if (!cret) return -2;
1395 if (!listing) return -2;
1396 if (*listing) return -2;
1398 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1403 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1408 if (!cret) return -2;
1409 if (!name) return -2;
1411 sprintf(aaa, "CFLR %s|%d", name, for_real);
1412 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1418 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1422 if (!cret) return -1;
1423 if (floornum < 0) return -1;
1425 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1426 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1431 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1436 if (!cret) return -2;
1437 if (!floorname) return -2;
1438 if (floornum < 0) return -2;
1440 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1441 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1449 * You only need to fill out hostname, the defaults will be used if any of the
1450 * other fields are not set properly.
1452 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1453 int revision, const char *software_name, const char *hostname,
1459 if (developerid < 0 || clientid < 0 || revision < 0 ||
1463 revision = REV_LEVEL - 600;
1464 software_name = "Citadel (libcitadel)";
1466 if (!hostname) return -2;
1468 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1469 if (!aaa) return -1;
1471 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1472 revision, software_name, hostname);
1473 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1480 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1486 if (!cret) return -2;
1487 if (!username) return -2;
1489 aaa = (char *)malloc(strlen(username) + 8);
1490 if (!aaa) return -1;
1493 sprintf(aaa, "SEXP %s|-", username);
1494 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1497 sprintf(aaa, "SEXP %s||", username);
1498 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1506 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1510 if (!cret) return -2;
1511 if (!listing) return -2;
1512 if (*listing) return -2;
1514 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1519 /* mode is 0 = enable, 1 = disable, 2 = status */
1520 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1524 if (!cret) return -2;
1526 sprintf(aaa, "DEXP %d", mode);
1527 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1532 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1534 if (!cret) return -2;
1535 if (!bio) return -2;
1537 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1543 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1549 if (!cret) return -2;
1550 if (!username) return -2;
1551 if (!listing) return -2;
1552 if (*listing) return -2;
1554 aaa = (char *)malloc(strlen(username) + 6);
1555 if (!aaa) return -1;
1557 sprintf(aaa, "RBIO %s", username);
1558 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1565 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1569 if (!cret) return -2;
1570 if (!listing) return -2;
1571 if (*listing) return -2;
1573 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1578 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1582 if (!cret) return -1;
1584 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1585 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1590 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1594 if (!cret) return -1;
1596 sprintf(aaa, "TERM %d", sid);
1597 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1602 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1604 if (!cret) return -1;
1606 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1611 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1615 if (!cret) return -1;
1617 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1618 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1623 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1629 if (!cret) return -2;
1630 if (!text) return -2;
1631 if (!filename) return -2;
1633 aaa = (char *)malloc(strlen(filename) + 6);
1634 if (!aaa) return -1;
1636 sprintf(aaa, "EMSG %s", filename);
1637 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1644 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1649 if (!cret) return -2;
1650 if (!hostname) return -2;
1652 aaa = (char *)malloc(strlen(hostname) + 6);
1653 if (!aaa) return -1;
1655 sprintf(aaa, "HCHG %s", hostname);
1656 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1663 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1668 if (!cret) return -2;
1669 if (!roomname) return -2;
1671 aaa = (char *)malloc(strlen(roomname) + 6);
1672 if (!aaa) return -1;
1674 sprintf(aaa, "RCHG %s", roomname);
1675 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1682 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1687 if (!cret) return -2;
1688 if (!username) return -2;
1690 aaa = (char *)malloc(strlen(username) + 6);
1691 if (!aaa) return -1;
1693 sprintf(aaa, "UCHG %s", username);
1694 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1701 /* This function returns the actual server time reported, or 0 if error */
1702 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1704 register time_t tret;
1707 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1708 if (ret / 100 == 2) {
1709 tret = extract_long(cret, 0);
1718 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1719 struct ctdluser **uret, char *cret)
1724 if (!cret) return -2;
1725 if (!uret) return -2;
1726 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1727 if (!*uret) return -1;
1729 sprintf(aaa, "AGUP %s", who);
1730 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1732 if (ret / 100 == 2) {
1733 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1734 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1735 uret[0]->flags = extract_int(cret, 2);
1736 uret[0]->timescalled = extract_long(cret, 3);
1737 uret[0]->posted = extract_long(cret, 4);
1738 uret[0]->axlevel = extract_int(cret, 5);
1739 uret[0]->usernum = extract_long(cret, 6);
1740 uret[0]->lastcall = extract_long(cret, 7);
1741 uret[0]->USuserpurge = extract_int(cret, 8);
1748 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1753 if (!cret) return -2;
1754 if (!uret) return -2;
1756 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1757 if (!aaa) return -1;
1759 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1760 uret->fullname, uret->password, uret->flags,
1761 uret->timescalled, uret->posted, uret->axlevel,
1762 uret->usernum, uret->lastcall, uret->USuserpurge);
1763 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1770 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1771 /* caller must free the struct ExpirePolicy */
1772 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1773 struct ExpirePolicy **policy, char *cret)
1775 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1779 if (!cret) return -2;
1780 if (!policy) return -2;
1781 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1782 if (!*policy) return -1;
1783 if (which < 0 || which > 3) return -2;
1785 sprintf(cmd, "GPEX %s", proto[which]);
1786 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1787 if (ret / 100 == 2) {
1788 policy[0]->expire_mode = extract_int(cret, 0);
1789 policy[0]->expire_value = extract_int(cret, 1);
1796 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1797 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1798 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1799 struct ExpirePolicy *policy, char *cret)
1802 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1804 if (!cret) return -2;
1805 if (which < 0 || which > 3) return -2;
1806 if (!policy) return -2;
1807 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1808 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1810 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1811 policy->expire_mode, policy->expire_value);
1812 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1817 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1821 if (!cret) return -2;
1822 if (!listing) return -2;
1823 if (*listing) return -2;
1825 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1826 listing, &bytes, cret);
1831 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1833 if (!cret) return -2;
1834 if (!listing) return -2;
1836 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1842 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1843 char **listing, char *cret)
1849 if (!cret) return -2;
1850 if (!mimetype) return -2;
1851 if (!listing) return -2;
1852 if (*listing) return -2;
1854 aaa = malloc(strlen(mimetype) + 13);
1855 if (!aaa) return -1;
1856 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1857 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1858 listing, &bytes, cret);
1865 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1866 const char *listing, char *cret)
1871 if (!cret) return -2;
1872 if (!mimetype) return -2;
1873 if (!listing) return -2;
1875 aaa = malloc(strlen(mimetype) + 13);
1876 if (!aaa) return -1;
1877 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1878 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1886 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1890 if (!cret) return -2;
1891 if (!listing) return -2;
1892 if (*listing) return -2;
1894 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1895 listing, &bytes, cret);
1900 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1902 if (!cret) return -2;
1903 if (!listing) return -2;
1905 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1911 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1915 if (!cret) return -2;
1916 if (session < 0) return -2;
1918 sprintf(aaa, "REQT %d", session);
1919 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1924 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1928 if (!cret) return -2;
1929 if (msgnum < 0) return -2;
1931 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1932 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1937 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1946 /* New SSL object */
1947 temp_ssl = SSL_new(ssl_ctx);
1949 error_printf("SSL_new failed: %s\n",
1950 ERR_reason_error_string(ERR_get_error()));
1953 /* Pointless flag waving */
1954 #if SSLEAY_VERSION_NUMBER >= 0x0922
1955 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1958 if (!access(EGD_POOL, F_OK))
1961 if (!RAND_status()) {
1962 error_printf("PRNG not properly seeded\n");
1966 /* Associate network connection with SSL object */
1967 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1968 error_printf("SSL_set_fd failed: %s\n",
1969 ERR_reason_error_string(ERR_get_error()));
1973 if (status_hook != NULL)
1974 status_hook("Requesting encryption...\r");
1976 /* Ready to start SSL/TLS */
1978 CtdlIPC_putline(ipc, "STLS");
1979 CtdlIPC_getline(ipc, buf);
1980 if (buf[0] != '2') {
1981 error_printf("Server can't start TLS: %s\n", buf);
1985 r = CtdlIPCGenericCommand(ipc,
1986 "STLS", NULL, 0, NULL, NULL, cret);
1988 error_printf("Server can't start TLS: %s\n", buf);
1993 /* Do SSL/TLS handshake */
1994 if ((a = SSL_connect(temp_ssl)) < 1) {
1995 error_printf("SSL_connect failed: %s\n",
1996 ERR_reason_error_string(ERR_get_error()));
2000 ipc->ssl = temp_ssl;
2002 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2006 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2007 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2008 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2009 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2015 #endif /* HAVE_OPENSSL */
2020 static void endtls(SSL *ssl)
2031 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2036 if (!address) return -2;
2037 if (!cret) return -2;
2039 aaa = (char *)malloc(strlen(address) + 6);
2040 if (!aaa) return -1;
2042 sprintf(aaa, "QDIR %s", address);
2043 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2050 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2054 if (!cret) return -2;
2055 sprintf(aaa, "IPGM %d", secret);
2056 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2061 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2065 if (!cret) return -2;
2066 if (!mret) return -2;
2067 if (*mret) return -2;
2069 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2090 /* ************************************************************************** */
2091 /* Stuff below this line is not for public consumption */
2092 /* ************************************************************************** */
2095 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2097 if (ipc->network_status_cb) ipc->network_status_cb(1);
2098 #ifdef THREADED_CLIENT
2099 pthread_mutex_lock(&(ipc->mutex));
2104 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2106 #ifdef THREADED_CLIENT
2107 pthread_mutex_unlock(&(ipc->mutex));
2109 if (ipc->network_status_cb) ipc->network_status_cb(0);
2113 /* Read a listing from the server up to 000. Append to dest if it exists */
2114 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2123 length = strlen(ret);
2128 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2129 linelength = strlen(aaa);
2130 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2132 strcpy(&ret[length], aaa);
2133 length += linelength;
2134 strcpy(&ret[length++], "\n");
2142 /* Send a listing to the server; generate the ending 000. */
2143 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2147 text = (char *)malloc(strlen(listing) + 6);
2149 strcpy(text, listing);
2150 while (text[strlen(text) - 1] == '\n')
2151 text[strlen(text) - 1] = '\0';
2152 strcat(text, "\n000");
2153 CtdlIPC_putline(ipc, text);
2157 /* Malloc failed but we are committed to send */
2158 /* This may result in extra blanks at the bottom */
2159 CtdlIPC_putline(ipc, text);
2160 CtdlIPC_putline(ipc, "000");
2166 /* Partial read of file from server */
2167 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2169 register size_t len = 0;
2173 if (!cret) return 0;
2174 if (bytes < 1) return 0;
2177 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2178 CtdlIPC_putline(ipc, aaa);
2179 CtdlIPC_getline(ipc, aaa);
2181 strcpy(cret, &aaa[4]);
2183 len = extract_long(&aaa[4], 0);
2184 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2186 /* I know what I'm doing */
2187 serv_read(ipc, ((char *)(*buf) + offset), len);
2189 /* We have to read regardless */
2190 serv_read(ipc, aaa, len);
2194 CtdlIPC_unlock(ipc);
2200 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2204 if (!cret) return -2;
2205 if (!ipc->downloading) return -2;
2207 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2209 ipc->downloading = 0;
2215 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2219 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2220 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2227 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2228 void (*progress_gauge_callback)
2229 (CtdlIPC*, unsigned long, unsigned long),
2232 register size_t len;
2234 if (!cret) return -1;
2235 if (!buf) return -1;
2236 if (*buf) return -1;
2237 if (!ipc->downloading) return -1;
2240 if (progress_gauge_callback)
2241 progress_gauge_callback(ipc, len, bytes);
2242 while (len < bytes) {
2243 register size_t block;
2245 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2251 if (progress_gauge_callback)
2252 progress_gauge_callback(ipc, len, bytes);
2257 /* READ - pipelined */
2258 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2260 void (*progress_gauge_callback)
2261 (CtdlIPC*, unsigned long, unsigned long),
2264 register size_t len;
2265 register int calls; /* How many calls in the pipeline */
2266 register int i; /* iterator */
2269 if (!cret) return -1;
2270 if (!buf) return -1;
2271 if (*buf) return -1;
2272 if (!ipc->downloading) return -1;
2274 *buf = (void *)realloc(*buf, bytes - resume);
2275 if (!*buf) return -1;
2279 if (progress_gauge_callback)
2280 progress_gauge_callback(ipc, len, bytes);
2282 /* How many calls will be in the pipeline? */
2283 calls = (bytes - resume) / 4096;
2284 if ((bytes - resume) % 4096) calls++;
2286 /* Send all requests at once */
2287 for (i = 0; i < calls; i++) {
2288 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2289 CtdlIPC_putline(ipc, aaa);
2292 /* Receive all responses at once */
2293 for (i = 0; i < calls; i++) {
2294 CtdlIPC_getline(ipc, aaa);
2296 strcpy(cret, &aaa[4]);
2298 len = extract_long(&aaa[4], 0);
2299 /* I know what I'm doing */
2300 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2302 if (progress_gauge_callback)
2303 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2305 CtdlIPC_unlock(ipc);
2311 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2316 if (!cret) return -1;
2317 if (!ipc->uploading) return -1;
2319 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2320 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2327 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2328 void (*progress_gauge_callback)
2329 (CtdlIPC*, unsigned long, unsigned long),
2332 register int ret = -1;
2333 register size_t offset = 0;
2339 if (!cret) return -1;
2340 if (!path) return -1;
2341 if (!*path) return -1;
2343 fd = fopen(path, "r");
2346 fseek(fd, 0L, SEEK_END);
2350 if (progress_gauge_callback)
2351 progress_gauge_callback(ipc, 0, bytes);
2353 while (offset < bytes) {
2354 register size_t to_write;
2356 /* Read some data in */
2357 to_write = fread(buf, 1, 4096, fd);
2359 if (feof(fd) || ferror(fd)) break;
2361 sprintf(aaa, "WRIT %d", (int)to_write);
2362 CtdlIPC_putline(ipc, aaa);
2363 CtdlIPC_getline(ipc, aaa);
2364 strcpy(cret, &aaa[4]);
2366 if (aaa[0] == '7') {
2367 to_write = extract_long(&aaa[4], 0);
2369 serv_write(ipc, buf, to_write);
2371 if (progress_gauge_callback)
2372 progress_gauge_callback(ipc, offset, bytes);
2373 /* Detect short reads and back up if needed */
2374 /* offset will never be negative anyway */
2375 fseek(fd, (signed)offset, SEEK_SET);
2380 if (progress_gauge_callback)
2381 progress_gauge_callback(ipc, 1, 1);
2382 return (!ferror(fd) ? ret : -2);
2387 * Generic command method. This method should handle any server command
2388 * except for CHAT. It takes the following arguments:
2390 * ipc The server to speak with
2391 * command Preformatted command to send to server
2392 * to_send A text or binary file to send to server
2393 * (only sent if server requests it)
2394 * bytes_to_send The number of bytes in to_send (required if
2395 * sending binary, optional if sending listing)
2396 * to_receive Pointer to a NULL pointer, if the server
2397 * sends text or binary we will allocate memory
2398 * for the file and stuff it here
2399 * bytes_to_receive If a file is received, we will store its
2401 * proto_response The protocol response. Caller must provide
2402 * this buffer and ensure that it is at least
2403 * 128 bytes in length.
2405 * This function returns a number equal to the protocol response number,
2406 * -1 if an internal error occurred, -2 if caller provided bad values,
2407 * or 0 - the protocol response number if bad values were found during
2408 * the protocol exchange.
2409 * It stores the protocol response string (minus the number) in
2410 * protocol_response as described above. Some commands send additional
2411 * data in this string.
2413 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2414 const char *command, const char *to_send,
2415 size_t bytes_to_send, char **to_receive,
2416 size_t *bytes_to_receive, char *proto_response)
2422 if (!command) return -2;
2423 if (!proto_response) return -2;
2426 if (ipc->ssl) watch_ssl = 1;
2430 CtdlIPC_putline(ipc, command);
2432 CtdlIPC_getline(ipc, proto_response);
2433 if (proto_response[3] == '*')
2435 ret = atoi(proto_response);
2436 strcpy(proto_response, &proto_response[4]);
2437 switch (ret / 100) {
2438 default: /* Unknown, punt */
2440 case 3: /* MORE_DATA */
2442 /* Don't need to do anything */
2444 case 1: /* LISTING_FOLLOWS */
2445 if (to_receive && !*to_receive && bytes_to_receive) {
2446 *to_receive = CtdlIPCReadListing(ipc, NULL);
2447 } else { /* Drain */
2448 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2452 case 4: /* SEND_LISTING */
2454 CtdlIPCSendListing(ipc, to_send);
2456 /* No listing given, fake it */
2457 CtdlIPC_putline(ipc, "000");
2461 case 6: /* BINARY_FOLLOWS */
2462 if (to_receive && !*to_receive && bytes_to_receive) {
2464 extract_long(proto_response, 0);
2465 *to_receive = (char *)
2466 malloc((size_t)*bytes_to_receive);
2470 serv_read(ipc, *to_receive,
2477 drain = extract_long(proto_response, 0);
2478 while (drain > SIZ) {
2479 serv_read(ipc, buf, SIZ);
2482 serv_read(ipc, buf, drain);
2486 case 7: /* SEND_BINARY */
2487 if (to_send && bytes_to_send) {
2488 serv_write(ipc, to_send, bytes_to_send);
2489 } else if (bytes_to_send) {
2490 /* Fake it, send nulls */
2493 fake = bytes_to_send;
2494 memset(buf, '\0', SIZ);
2495 while (fake > SIZ) {
2496 serv_write(ipc, buf, SIZ);
2499 serv_write(ipc, buf, fake);
2501 } /* else who knows? DANGER WILL ROBINSON */
2503 case 8: /* START_CHAT_MODE */
2504 if (!strncasecmp(command, "CHAT", 4)) {
2505 /* Don't call chatmode with generic! */
2506 CtdlIPC_putline(ipc, "/quit");
2509 /* In this mode we send then receive listing */
2511 CtdlIPCSendListing(ipc, to_send);
2513 /* No listing given, fake it */
2514 CtdlIPC_putline(ipc, "000");
2517 if (to_receive && !*to_receive
2518 && bytes_to_receive) {
2519 *to_receive = CtdlIPCReadListing(ipc, NULL);
2520 } else { /* Drain */
2521 while (CtdlIPC_getline(ipc, buf),
2522 strcmp(buf, "000")) ;
2527 case 9: /* ASYNC_MSG */
2528 /* CtdlIPCDoAsync(ret, proto_response); */
2529 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2535 CtdlIPC_unlock(ipc);
2540 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2542 struct hostent *phe;
2543 struct servent *pse;
2544 struct protoent *ppe;
2545 struct sockaddr_in sin;
2548 memset(&sin, 0, sizeof(sin));
2549 sin.sin_family = AF_INET;
2551 pse = getservbyname(service, protocol);
2553 sin.sin_port = pse->s_port;
2555 else if (atoi(service) > 0) {
2556 sin.sin_port = htons(atoi(service));
2559 sin.sin_port = htons(defaultPort);
2561 phe = gethostbyname(host);
2563 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2564 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2567 if ((ppe = getprotobyname(protocol)) == 0) {
2570 if (!strcmp(protocol, "udp")) {
2576 s = socket(PF_INET, type, ppe->p_proto);
2581 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2589 static int uds_connectsock(int *isLocal, char *sockpath)
2591 struct sockaddr_un addr;
2594 memset(&addr, 0, sizeof(addr));
2595 addr.sun_family = AF_UNIX;
2596 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2598 s = socket(AF_UNIX, SOCK_STREAM, 0);
2603 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2614 * input binary data from socket
2616 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2618 unsigned int len, rlen;
2620 #if defined(HAVE_OPENSSL)
2622 serv_read_ssl(ipc, buf, bytes);
2627 while (len < bytes) {
2628 rlen = read(ipc->sock, &buf[len], bytes - len);
2630 connection_died(ipc, 0);
2639 * send binary to server
2641 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2643 unsigned int bytes_written = 0;
2646 #if defined(HAVE_OPENSSL)
2648 serv_write_ssl(ipc, buf, nbytes);
2652 while (bytes_written < nbytes) {
2653 retval = write(ipc->sock, &buf[bytes_written],
2654 nbytes - bytes_written);
2656 connection_died(ipc, 0);
2659 bytes_written += retval;
2666 * input binary data from encrypted connection
2668 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2674 while (len < bytes) {
2675 if (SSL_want_read(ipc->ssl)) {
2676 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2677 error_printf("SSL_write in serv_read:\n");
2678 ERR_print_errors_fp(stderr);
2681 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2685 errval = SSL_get_error(ipc->ssl, rlen);
2686 if (errval == SSL_ERROR_WANT_READ ||
2687 errval == SSL_ERROR_WANT_WRITE) {
2692 Not sure why we'd want to handle these error codes any differently,
2693 but this definitely isn't the way to handle them. Someone must have
2694 naively assumed that we could fall back to unencrypted communications,
2695 but all it does is just recursively blow the stack.
2696 if (errval == SSL_ERROR_ZERO_RETURN ||
2697 errval == SSL_ERROR_SSL) {
2698 serv_read(ipc, &buf[len], bytes - len);
2702 error_printf("SSL_read in serv_read: %s\n",
2703 ERR_reason_error_string(ERR_peek_error()));
2704 connection_died(ipc, 1);
2713 * send binary to server encrypted
2715 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2717 unsigned int bytes_written = 0;
2721 while (bytes_written < nbytes) {
2722 if (SSL_want_write(ipc->ssl)) {
2723 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2724 error_printf("SSL_read in serv_write:\n");
2725 ERR_print_errors_fp(stderr);
2728 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2729 nbytes - bytes_written);
2733 errval = SSL_get_error(ipc->ssl, retval);
2734 if (errval == SSL_ERROR_WANT_READ ||
2735 errval == SSL_ERROR_WANT_WRITE) {
2739 if (errval == SSL_ERROR_ZERO_RETURN ||
2740 errval == SSL_ERROR_SSL) {
2741 serv_write(ipc, &buf[bytes_written],
2742 nbytes - bytes_written);
2745 error_printf("SSL_write in serv_write: %s\n",
2746 ERR_reason_error_string(ERR_peek_error()));
2747 connection_died(ipc, 1);
2750 bytes_written += retval;
2755 static void CtdlIPC_init_OpenSSL(void)
2758 SSL_METHOD *ssl_method;
2761 /* already done init */
2770 SSL_load_error_strings();
2771 SSLeay_add_ssl_algorithms();
2773 /* Set up the SSL context in which we will oeprate */
2774 ssl_method = SSLv23_client_method();
2775 ssl_ctx = SSL_CTX_new(ssl_method);
2777 error_printf("SSL_CTX_new failed: %s\n",
2778 ERR_reason_error_string(ERR_get_error()));
2781 /* Any reasonable cipher we can get */
2782 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2783 error_printf("No ciphers available for encryption\n");
2786 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2788 /* Load DH parameters into the context */
2791 error_printf("Can't allocate a DH object: %s\n",
2792 ERR_reason_error_string(ERR_get_error()));
2795 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2796 error_printf("Can't assign DH_P: %s\n",
2797 ERR_reason_error_string(ERR_get_error()));
2801 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2802 error_printf("Can't assign DH_G: %s\n",
2803 ERR_reason_error_string(ERR_get_error()));
2808 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2811 #ifdef THREADED_CLIENT
2812 /* OpenSSL requires callbacks for threaded clients */
2813 CRYPTO_set_locking_callback(ssl_lock);
2814 CRYPTO_set_id_callback(id_callback);
2816 /* OpenSSL requires us to do semaphores for threaded clients */
2817 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2819 perror("malloc failed");
2822 for (a = 0; a < CRYPTO_num_locks(); a++) {
2823 Critters[a] = malloc(sizeof (pthread_mutex_t));
2825 perror("malloc failed");
2828 pthread_mutex_init(Critters[a], NULL);
2831 #endif /* THREADED_CLIENT */
2835 static void ssl_lock(int mode, int n, const char *file, int line)
2837 #ifdef THREADED_CLIENT
2838 if (mode & CRYPTO_LOCK)
2839 pthread_mutex_lock(Critters[n]);
2841 pthread_mutex_unlock(Critters[n]);
2842 #endif /* THREADED_CLIENT */
2845 #ifdef THREADED_CLIENT
2846 static unsigned long id_callback(void) {
2847 return (unsigned long)pthread_self();
2849 #endif /* THREADED_CLIENT */
2850 #endif /* HAVE_OPENSSL */
2854 * input string from socket - implemented in terms of serv_read()
2856 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2860 /* Read one character at a time. */
2862 serv_read(ipc, &buf[i], 1);
2863 if (buf[i] == '\n' || i == (SIZ-1))
2867 /* If we got a long line, discard characters until the newline. */
2869 while (buf[i] != '\n')
2870 serv_read(ipc, &buf[i], 1);
2872 /* Strip the trailing newline (and carriage return, if present) */
2873 if (buf[i] == 10) buf[i--] = 0;
2874 if (buf[i] == 13) buf[i--] = 0;
2877 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2879 CtdlIPC_getline(ipc, buf);
2883 * send line to server - implemented in terms of serv_write()
2885 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2891 cmd = malloc(len + 2);
2893 /* This requires no extra memory */
2894 serv_write(ipc, buf, len);
2895 serv_write(ipc, "\n", 1);
2897 /* This is network-optimized */
2898 strncpy(cmd, buf, len);
2899 strcpy(cmd + len, "\n");
2900 serv_write(ipc, cmd, len + 1);
2904 ipc->last_command_sent = time(NULL);
2907 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
2909 CtdlIPC_putline(ipc, buf);
2916 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2924 ipc = ialloc(CtdlIPC);
2928 #if defined(HAVE_OPENSSL)
2930 CtdlIPC_init_OpenSSL();
2932 #if defined(HAVE_PTHREAD_H)
2933 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2935 ipc->sock = -1; /* Not connected */
2936 ipc->isLocal = 0; /* Not local, of course! */
2937 ipc->downloading = 0;
2939 ipc->last_command_sent = 0L;
2940 ipc->network_status_cb = NULL;
2942 strcpy(cithost, DEFAULT_HOST); /* default host */
2943 strcpy(citport, DEFAULT_PORT); /* default port */
2945 /* Allow caller to supply our values (Windows) */
2946 if (hostbuf && strlen(hostbuf) > 0)
2947 strcpy(cithost, hostbuf);
2948 if (portbuf && strlen(portbuf) > 0)
2949 strcpy(citport, portbuf);
2951 /* Read host/port from command line if present */
2952 for (a = 0; a < argc; ++a) {
2955 } else if (a == 1) {
2956 strcpy(cithost, argv[a]);
2957 } else if (a == 2) {
2958 strcpy(citport, argv[a]);
2960 error_printf("%s: usage: ",argv[0]);
2961 error_printf("%s [host] [port] ",argv[0]);
2968 if ((!strcmp(cithost, "localhost"))
2969 || (!strcmp(cithost, "127.0.0.1"))) {
2973 /* If we're using a unix domain socket we can do a bunch of stuff */
2974 if (!strcmp(cithost, UDS)) {
2975 if (!strcasecmp(citport, DEFAULT_PORT)) {
2976 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
2979 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
2981 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2982 if (ipc->sock == -1) {
2986 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2987 if (portbuf != NULL) strcpy(portbuf, sockpath);
2991 ipc->sock = connectsock(cithost, citport, "tcp", 504);
2992 if (ipc->sock == -1) {
2996 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2997 if (portbuf != NULL) strcpy(portbuf, citport);
3003 * Disconnect and delete the IPC class (destructor)
3005 void CtdlIPC_delete(CtdlIPC* ipc)
3009 SSL_shutdown(ipc->ssl);
3014 if (ipc->sock > -1) {
3015 shutdown(ipc->sock, 2); /* Close it up */
3023 * Disconnect and delete the IPC class (destructor)
3024 * Also NULLs out the pointer
3026 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3028 CtdlIPC_delete(*pipc);
3034 * return the file descriptor of the server socket so we can select() on it.
3036 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3039 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3046 * return one character
3048 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3051 char CtdlIPC_get(CtdlIPC* ipc)
3056 serv_read(ipc, buf, 1);