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;
2340 if (!cret) return -1;
2341 if (!path) return -1;
2342 if (!*path) return -1;
2344 fd = fopen(path, "r");
2347 fseek(fd, 0L, SEEK_END);
2351 if (progress_gauge_callback)
2352 progress_gauge_callback(ipc, 0, bytes);
2354 while (offset < bytes) {
2355 register size_t to_write;
2357 /* Read some data in */
2358 to_write = fread(buf, 1, 4096, fd);
2360 if (feof(fd) || ferror(fd)) break;
2362 sprintf(aaa, "WRIT %d", (int)to_write);
2363 CtdlIPC_putline(ipc, aaa);
2364 CtdlIPC_getline(ipc, aaa);
2365 strcpy(cret, &aaa[4]);
2367 if (aaa[0] == '7') {
2368 to_write = extract_long(&aaa[4], 0);
2370 serv_write(ipc, buf, to_write);
2372 if (progress_gauge_callback)
2373 progress_gauge_callback(ipc, offset, bytes);
2374 /* Detect short reads and back up if needed */
2375 /* offset will never be negative anyway */
2376 fseek(fd, (signed)offset, SEEK_SET);
2381 if (progress_gauge_callback)
2382 progress_gauge_callback(ipc, 1, 1);
2385 return (!ferr ? ret : -2);
2390 * Generic command method. This method should handle any server command
2391 * except for CHAT. It takes the following arguments:
2393 * ipc The server to speak with
2394 * command Preformatted command to send to server
2395 * to_send A text or binary file to send to server
2396 * (only sent if server requests it)
2397 * bytes_to_send The number of bytes in to_send (required if
2398 * sending binary, optional if sending listing)
2399 * to_receive Pointer to a NULL pointer, if the server
2400 * sends text or binary we will allocate memory
2401 * for the file and stuff it here
2402 * bytes_to_receive If a file is received, we will store its
2404 * proto_response The protocol response. Caller must provide
2405 * this buffer and ensure that it is at least
2406 * 128 bytes in length.
2408 * This function returns a number equal to the protocol response number,
2409 * -1 if an internal error occurred, -2 if caller provided bad values,
2410 * or 0 - the protocol response number if bad values were found during
2411 * the protocol exchange.
2412 * It stores the protocol response string (minus the number) in
2413 * protocol_response as described above. Some commands send additional
2414 * data in this string.
2416 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2417 const char *command, const char *to_send,
2418 size_t bytes_to_send, char **to_receive,
2419 size_t *bytes_to_receive, char *proto_response)
2425 if (!command) return -2;
2426 if (!proto_response) return -2;
2429 if (ipc->ssl) watch_ssl = 1;
2433 CtdlIPC_putline(ipc, command);
2435 CtdlIPC_getline(ipc, proto_response);
2436 if (proto_response[3] == '*')
2438 ret = atoi(proto_response);
2439 strcpy(proto_response, &proto_response[4]);
2440 switch (ret / 100) {
2441 default: /* Unknown, punt */
2443 case 3: /* MORE_DATA */
2445 /* Don't need to do anything */
2447 case 1: /* LISTING_FOLLOWS */
2448 if (to_receive && !*to_receive && bytes_to_receive) {
2449 *to_receive = CtdlIPCReadListing(ipc, NULL);
2450 } else { /* Drain */
2451 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2455 case 4: /* SEND_LISTING */
2457 CtdlIPCSendListing(ipc, to_send);
2459 /* No listing given, fake it */
2460 CtdlIPC_putline(ipc, "000");
2464 case 6: /* BINARY_FOLLOWS */
2465 if (to_receive && !*to_receive && bytes_to_receive) {
2467 extract_long(proto_response, 0);
2468 *to_receive = (char *)
2469 malloc((size_t)*bytes_to_receive);
2473 serv_read(ipc, *to_receive,
2480 drain = extract_long(proto_response, 0);
2481 while (drain > SIZ) {
2482 serv_read(ipc, buf, SIZ);
2485 serv_read(ipc, buf, drain);
2489 case 7: /* SEND_BINARY */
2490 if (to_send && bytes_to_send) {
2491 serv_write(ipc, to_send, bytes_to_send);
2492 } else if (bytes_to_send) {
2493 /* Fake it, send nulls */
2496 fake = bytes_to_send;
2497 memset(buf, '\0', SIZ);
2498 while (fake > SIZ) {
2499 serv_write(ipc, buf, SIZ);
2502 serv_write(ipc, buf, fake);
2504 } /* else who knows? DANGER WILL ROBINSON */
2506 case 8: /* START_CHAT_MODE */
2507 if (!strncasecmp(command, "CHAT", 4)) {
2508 /* Don't call chatmode with generic! */
2509 CtdlIPC_putline(ipc, "/quit");
2512 /* In this mode we send then receive listing */
2514 CtdlIPCSendListing(ipc, to_send);
2516 /* No listing given, fake it */
2517 CtdlIPC_putline(ipc, "000");
2520 if (to_receive && !*to_receive
2521 && bytes_to_receive) {
2522 *to_receive = CtdlIPCReadListing(ipc, NULL);
2523 } else { /* Drain */
2524 while (CtdlIPC_getline(ipc, buf),
2525 strcmp(buf, "000")) ;
2530 case 9: /* ASYNC_MSG */
2531 /* CtdlIPCDoAsync(ret, proto_response); */
2532 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2538 CtdlIPC_unlock(ipc);
2543 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2545 struct hostent *phe;
2546 struct servent *pse;
2547 struct protoent *ppe;
2548 struct sockaddr_in sin;
2551 memset(&sin, 0, sizeof(sin));
2552 sin.sin_family = AF_INET;
2554 pse = getservbyname(service, protocol);
2556 sin.sin_port = pse->s_port;
2558 else if (atoi(service) > 0) {
2559 sin.sin_port = htons(atoi(service));
2562 sin.sin_port = htons(defaultPort);
2564 phe = gethostbyname(host);
2566 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2567 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2570 if ((ppe = getprotobyname(protocol)) == 0) {
2573 if (!strcmp(protocol, "udp")) {
2579 s = socket(PF_INET, type, ppe->p_proto);
2584 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2592 static int uds_connectsock(int *isLocal, char *sockpath)
2594 struct sockaddr_un addr;
2597 memset(&addr, 0, sizeof(addr));
2598 addr.sun_family = AF_UNIX;
2599 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2601 s = socket(AF_UNIX, SOCK_STREAM, 0);
2606 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2617 * input binary data from socket
2619 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2621 unsigned int len, rlen;
2623 #if defined(HAVE_OPENSSL)
2625 serv_read_ssl(ipc, buf, bytes);
2630 while (len < bytes) {
2631 rlen = read(ipc->sock, &buf[len], bytes - len);
2633 connection_died(ipc, 0);
2642 * send binary to server
2644 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2646 unsigned int bytes_written = 0;
2649 #if defined(HAVE_OPENSSL)
2651 serv_write_ssl(ipc, buf, nbytes);
2655 while (bytes_written < nbytes) {
2656 retval = write(ipc->sock, &buf[bytes_written],
2657 nbytes - bytes_written);
2659 connection_died(ipc, 0);
2662 bytes_written += retval;
2669 * input binary data from encrypted connection
2671 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2677 while (len < bytes) {
2678 if (SSL_want_read(ipc->ssl)) {
2679 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2680 error_printf("SSL_write in serv_read:\n");
2681 ERR_print_errors_fp(stderr);
2684 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2688 errval = SSL_get_error(ipc->ssl, rlen);
2689 if (errval == SSL_ERROR_WANT_READ ||
2690 errval == SSL_ERROR_WANT_WRITE) {
2695 Not sure why we'd want to handle these error codes any differently,
2696 but this definitely isn't the way to handle them. Someone must have
2697 naively assumed that we could fall back to unencrypted communications,
2698 but all it does is just recursively blow the stack.
2699 if (errval == SSL_ERROR_ZERO_RETURN ||
2700 errval == SSL_ERROR_SSL) {
2701 serv_read(ipc, &buf[len], bytes - len);
2705 error_printf("SSL_read in serv_read: %s\n",
2706 ERR_reason_error_string(ERR_peek_error()));
2707 connection_died(ipc, 1);
2716 * send binary to server encrypted
2718 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2720 unsigned int bytes_written = 0;
2724 while (bytes_written < nbytes) {
2725 if (SSL_want_write(ipc->ssl)) {
2726 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2727 error_printf("SSL_read in serv_write:\n");
2728 ERR_print_errors_fp(stderr);
2731 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2732 nbytes - bytes_written);
2736 errval = SSL_get_error(ipc->ssl, retval);
2737 if (errval == SSL_ERROR_WANT_READ ||
2738 errval == SSL_ERROR_WANT_WRITE) {
2742 if (errval == SSL_ERROR_ZERO_RETURN ||
2743 errval == SSL_ERROR_SSL) {
2744 serv_write(ipc, &buf[bytes_written],
2745 nbytes - bytes_written);
2748 error_printf("SSL_write in serv_write: %s\n",
2749 ERR_reason_error_string(ERR_peek_error()));
2750 connection_died(ipc, 1);
2753 bytes_written += retval;
2758 static void CtdlIPC_init_OpenSSL(void)
2761 SSL_METHOD *ssl_method;
2764 /* already done init */
2773 SSL_load_error_strings();
2774 SSLeay_add_ssl_algorithms();
2776 /* Set up the SSL context in which we will oeprate */
2777 ssl_method = SSLv23_client_method();
2778 ssl_ctx = SSL_CTX_new(ssl_method);
2780 error_printf("SSL_CTX_new failed: %s\n",
2781 ERR_reason_error_string(ERR_get_error()));
2784 /* Any reasonable cipher we can get */
2785 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2786 error_printf("No ciphers available for encryption\n");
2789 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2791 /* Load DH parameters into the context */
2794 error_printf("Can't allocate a DH object: %s\n",
2795 ERR_reason_error_string(ERR_get_error()));
2798 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2799 error_printf("Can't assign DH_P: %s\n",
2800 ERR_reason_error_string(ERR_get_error()));
2804 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2805 error_printf("Can't assign DH_G: %s\n",
2806 ERR_reason_error_string(ERR_get_error()));
2811 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2814 #ifdef THREADED_CLIENT
2815 /* OpenSSL requires callbacks for threaded clients */
2816 CRYPTO_set_locking_callback(ssl_lock);
2817 CRYPTO_set_id_callback(id_callback);
2819 /* OpenSSL requires us to do semaphores for threaded clients */
2820 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2822 perror("malloc failed");
2825 for (a = 0; a < CRYPTO_num_locks(); a++) {
2826 Critters[a] = malloc(sizeof (pthread_mutex_t));
2828 perror("malloc failed");
2831 pthread_mutex_init(Critters[a], NULL);
2834 #endif /* THREADED_CLIENT */
2838 static void ssl_lock(int mode, int n, const char *file, int line)
2840 #ifdef THREADED_CLIENT
2841 if (mode & CRYPTO_LOCK)
2842 pthread_mutex_lock(Critters[n]);
2844 pthread_mutex_unlock(Critters[n]);
2845 #endif /* THREADED_CLIENT */
2848 #ifdef THREADED_CLIENT
2849 static unsigned long id_callback(void) {
2850 return (unsigned long)pthread_self();
2852 #endif /* THREADED_CLIENT */
2853 #endif /* HAVE_OPENSSL */
2857 * input string from socket - implemented in terms of serv_read()
2859 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2863 /* Read one character at a time. */
2865 serv_read(ipc, &buf[i], 1);
2866 if (buf[i] == '\n' || i == (SIZ-1))
2870 /* If we got a long line, discard characters until the newline. */
2872 while (buf[i] != '\n')
2873 serv_read(ipc, &buf[i], 1);
2875 /* Strip the trailing newline (and carriage return, if present) */
2876 if (i>=0 && buf[i] == 10) buf[i--] = 0;
2877 if (i>=0 && buf[i] == 13) buf[i--] = 0;
2880 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2882 CtdlIPC_getline(ipc, buf);
2886 * send line to server - implemented in terms of serv_write()
2888 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2894 cmd = malloc(len + 2);
2896 /* This requires no extra memory */
2897 serv_write(ipc, buf, len);
2898 serv_write(ipc, "\n", 1);
2900 /* This is network-optimized */
2901 strncpy(cmd, buf, len);
2902 strcpy(cmd + len, "\n");
2903 serv_write(ipc, cmd, len + 1);
2907 ipc->last_command_sent = time(NULL);
2910 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
2912 CtdlIPC_putline(ipc, buf);
2919 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2927 ipc = ialloc(CtdlIPC);
2931 #if defined(HAVE_OPENSSL)
2933 CtdlIPC_init_OpenSSL();
2935 #if defined(HAVE_PTHREAD_H)
2936 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2938 ipc->sock = -1; /* Not connected */
2939 ipc->isLocal = 0; /* Not local, of course! */
2940 ipc->downloading = 0;
2942 ipc->last_command_sent = 0L;
2943 ipc->network_status_cb = NULL;
2945 strcpy(cithost, DEFAULT_HOST); /* default host */
2946 strcpy(citport, DEFAULT_PORT); /* default port */
2948 /* Allow caller to supply our values (Windows) */
2949 if (hostbuf && strlen(hostbuf) > 0)
2950 strcpy(cithost, hostbuf);
2951 if (portbuf && strlen(portbuf) > 0)
2952 strcpy(citport, portbuf);
2954 /* Read host/port from command line if present */
2955 for (a = 0; a < argc; ++a) {
2958 } else if (a == 1) {
2959 strcpy(cithost, argv[a]);
2960 } else if (a == 2) {
2961 strcpy(citport, argv[a]);
2963 error_printf("%s: usage: ",argv[0]);
2964 error_printf("%s [host] [port] ",argv[0]);
2971 if ((!strcmp(cithost, "localhost"))
2972 || (!strcmp(cithost, "127.0.0.1"))) {
2976 /* If we're using a unix domain socket we can do a bunch of stuff */
2977 if (!strcmp(cithost, UDS)) {
2978 if (!strcasecmp(citport, DEFAULT_PORT)) {
2979 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
2982 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
2984 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2985 if (ipc->sock == -1) {
2989 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2990 if (portbuf != NULL) strcpy(portbuf, sockpath);
2994 ipc->sock = connectsock(cithost, citport, "tcp", 504);
2995 if (ipc->sock == -1) {
2999 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3000 if (portbuf != NULL) strcpy(portbuf, citport);
3006 * Disconnect and delete the IPC class (destructor)
3008 void CtdlIPC_delete(CtdlIPC* ipc)
3012 SSL_shutdown(ipc->ssl);
3017 if (ipc->sock > -1) {
3018 shutdown(ipc->sock, 2); /* Close it up */
3026 * Disconnect and delete the IPC class (destructor)
3027 * Also NULLs out the pointer
3029 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3031 CtdlIPC_delete(*pipc);
3037 * return the file descriptor of the server socket so we can select() on it.
3039 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3042 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3049 * return one character
3051 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3054 char CtdlIPC_get(CtdlIPC* ipc)
3059 serv_read(ipc, buf, 1);