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 endtls(SSL *ssl);
75 #ifdef THREADED_CLIENT
76 static unsigned long id_callback(void);
77 #endif /* THREADED_CLIENT */
78 #endif /* HAVE_OPENSSL */
79 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
80 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
84 * Does nothing. The server should always return 200.
86 int CtdlIPCNoop(CtdlIPC *ipc)
90 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
95 * Does nothing interesting. The server should always return 200
96 * along with your string.
98 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
104 if (!cret) return -2;
106 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
109 sprintf(aaa, "ECHO %s", arg);
110 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
117 * Asks the server to close the connecction.
118 * Should always return 200.
120 int CtdlIPCQuit(CtdlIPC *ipc)
122 register int ret = 221; /* Default to successful quit */
126 if (ipc->sock > -1) {
127 CtdlIPC_putline(ipc, "QUIT");
128 CtdlIPC_getline(ipc, aaa);
133 SSL_shutdown(ipc->ssl);
137 shutdown(ipc->sock, 2); /* Close connection; we're dead */
145 * Asks the server to logout. Should always return 200, even if no user
146 * was logged in. The user will not be logged in after this!
148 int CtdlIPCLogout(CtdlIPC *ipc)
154 CtdlIPC_putline(ipc, "LOUT");
155 CtdlIPC_getline(ipc, aaa);
163 * First stage of authentication - pass the username. Returns 300 if the
164 * username is able to log in, with the username correctly spelled in cret.
165 * Returns various 500 error codes if the user doesn't exist, etc.
167 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
172 if (!username) return -2;
173 if (!cret) return -2;
175 aaa = (char *)malloc((size_t)(strlen(username) + 6));
178 sprintf(aaa, "USER %s", username);
179 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
186 * Second stage of authentication - provide password. The server returns
187 * 200 and several arguments in cret relating to the user's account.
189 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
194 if (!passwd) return -2;
195 if (!cret) return -2;
197 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
200 sprintf(aaa, "PASS %s", passwd);
201 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
208 * Second stage of authentication - provide password. The server returns
209 * 200 and several arguments in cret relating to the user's account.
211 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
216 if (!response) return -2;
217 if (!cret) return -2;
219 aaa = (char *)malloc((size_t)(strlen(response) + 6));
222 sprintf(aaa, "PAS2 %s", response);
223 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
230 * Create a new user. This returns 200 plus the same arguments as TryPassword
231 * if selfservice is nonzero, unless there was a problem creating the account.
232 * If selfservice is zero, creates a new user but does not log out the existing
233 * user - intended for use by system administrators to create accounts on
234 * behalf of other users.
236 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
241 if (!username) return -2;
242 if (!cret) return -2;
244 aaa = (char *)malloc((size_t)(strlen(username) + 6));
247 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
248 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
255 * Changes the user's password. Returns 200 if changed, errors otherwise.
257 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
262 if (!passwd) return -2;
263 if (!cret) return -2;
265 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
268 sprintf(aaa, "SETP %s", passwd);
269 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
276 /* Caller must free the march list */
277 /* Room types are defined in enum RoomList; keep these in sync! */
278 /* floor is -1 for all, or floornum */
279 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
282 struct march *march = NULL;
283 static char *proto[] =
284 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
289 if (!listing) return -2;
290 if (*listing) return -2; /* Free the listing first */
291 if (!cret) return -2;
292 /* if (which < 0 || which > 4) return -2; */
293 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
295 sprintf(aaa, "%s %d", proto[which], floor);
296 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
297 if (ret / 100 == 1) {
300 while (bbb && strlen(bbb)) {
303 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
305 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
306 mptr = (struct march *) malloc(sizeof (struct march));
309 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
310 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
311 mptr->march_floor = (char) extract_int(aaa, 2);
312 mptr->march_order = (char) extract_int(aaa, 3);
313 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
314 mptr->march_access = (char) extract_int(aaa, 5);
321 while (mptr2->next != NULL)
335 /* Caller must free the struct ctdluser; caller may pass an existing one */
336 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
340 if (!cret) return -2;
341 if (!uret) return -2;
342 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
343 if (!*uret) return -1;
345 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
346 if (ret / 100 == 2) {
347 uret[0]->USscreenwidth = extract_int(cret, 0);
348 uret[0]->USscreenheight = extract_int(cret, 1);
349 uret[0]->flags = extract_int(cret, 2);
356 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
360 if (!uret) return -2;
361 if (!cret) return -2;
363 sprintf(aaa, "SETU %d|%d|%d",
364 uret->USscreenwidth, uret->USscreenheight,
366 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
371 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
372 struct ctdlipcroom **rret, char *cret)
377 if (!cret) return -2;
378 if (!rret) return -2;
379 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
380 if (!*rret) return -1;
383 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
388 sprintf(aaa, "GOTO %s|%s", room, passwd);
390 aaa = (char *)malloc(strlen(room) + 6);
395 sprintf(aaa, "GOTO %s", room);
397 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
398 if (ret / 100 == 2) {
399 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
400 rret[0]->RRunread = extract_long(cret, 1);
401 rret[0]->RRtotal = extract_long(cret, 2);
402 rret[0]->RRinfoupdated = extract_int(cret, 3);
403 rret[0]->RRflags = extract_int(cret, 4);
404 rret[0]->RRhighest = extract_long(cret, 5);
405 rret[0]->RRlastread = extract_long(cret, 6);
406 rret[0]->RRismailbox = extract_int(cret, 7);
407 rret[0]->RRaide = extract_int(cret, 8);
408 rret[0]->RRnewmail = extract_long(cret, 9);
409 rret[0]->RRfloor = extract_int(cret, 10);
410 rret[0]->RRflags2 = extract_int(cret, 14);
421 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
422 /* whicharg is number of messages, applies to last, first, gt, lt */
423 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
424 const char *mtemplate, unsigned long **mret, char *cret)
427 register unsigned long count = 0;
428 static char *proto[] =
429 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
434 if (!cret) return -2;
435 if (!mret) return -2;
436 if (*mret) return -2;
437 if (which < 0 || which > 6) return -2;
440 sprintf(aaa, "MSGS %s||%d", proto[which],
441 (mtemplate) ? 1 : 0);
443 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
444 (mtemplate) ? 1 : 0);
445 if (mtemplate) count = strlen(mtemplate);
446 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
450 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
453 while (bbb && strlen(bbb)) {
454 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
455 remove_token(bbb, 0, '\n');
456 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
457 sizeof (unsigned long)));
459 (*mret)[count++] = atol(aaa);
471 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
472 struct ctdlipcmessage **mret, char *cret)
478 int multipart_hunting = 0;
479 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(encoding, "");
489 strcpy(mret[0]->content_type, "");
490 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
491 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
492 if (ret / 100 == 1) {
494 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
495 while (strlen(bbb) > 4 && bbb[4] == '=') {
496 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
497 remove_token(bbb, 0, '\n');
499 if (!strncasecmp(aaa, "nhdr=yes", 8))
501 else if (!strncasecmp(aaa, "from=", 5))
502 safestrncpy(mret[0]->author, &aaa[5], SIZ);
503 else if (!strncasecmp(aaa, "type=", 5))
504 mret[0]->type = atoi(&aaa[5]);
505 else if (!strncasecmp(aaa, "msgn=", 5))
506 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
507 else if (!strncasecmp(aaa, "subj=", 5))
508 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
509 else if (!strncasecmp(aaa, "rfca=", 5))
510 safestrncpy(mret[0]->email, &aaa[5], SIZ);
511 else if (!strncasecmp(aaa, "hnod=", 5))
512 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
513 else if (!strncasecmp(aaa, "room=", 5))
514 safestrncpy(mret[0]->room, &aaa[5], SIZ);
515 else if (!strncasecmp(aaa, "node=", 5))
516 safestrncpy(mret[0]->node, &aaa[5], SIZ);
517 else if (!strncasecmp(aaa, "rcpt=", 5))
518 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
519 else if (!strncasecmp(aaa, "time=", 5))
520 mret[0]->time = atol(&aaa[5]);
522 /* Multipart/alternative prefix & suffix strings help
523 * us to determine which part we want to download.
525 else if (!strncasecmp(aaa, "pref=", 5)) {
526 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
527 if (!strcasecmp(multipart_prefix,
528 "multipart/alternative")) {
532 else if (!strncasecmp(aaa, "suff=", 5)) {
533 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
534 if (!strcasecmp(multipart_prefix,
535 "multipart/alternative")) {
540 else if (!strncasecmp(aaa, "part=", 5)) {
541 struct parts *ptr, *chain;
543 ptr = (struct parts *)calloc(1, sizeof (struct parts));
546 /* Fill the buffers for the caller */
547 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
548 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
549 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
550 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
551 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
552 ptr->length = extract_long(&aaa[5], 5);
553 if (!mret[0]->attachments)
554 mret[0]->attachments = ptr;
556 chain = mret[0]->attachments;
562 /* Now handle multipart/alternative */
563 if (multipart_hunting > 0) {
564 if ( (!strcasecmp(ptr->mimetype,
566 || (!strcasecmp(ptr->mimetype,
568 strcpy(mret[0]->mime_chosen,
576 /* Eliminate "text\n" */
577 remove_token(bbb, 0, '\n');
579 /* If doing a MIME thing, pull out the extra headers */
582 if (!strncasecmp(bbb, "Content-type:", 13)) {
583 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
584 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
585 striplt(mret[0]->content_type);
587 /* strip out ";charset=" portion. FIXME do something with
588 * the charset (like... convert it) instead of just throwing
591 if (strstr(mret[0]->content_type, ";") != NULL) {
592 strcpy(strstr(mret[0]->content_type, ";"), "");
596 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
597 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
598 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
599 striplt(mret[0]->mime_chosen);
601 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
602 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
603 strcpy(encoding, &encoding[26]);
606 remove_token(bbb, 0, '\n');
607 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
608 remove_token(bbb, 0, '\n');
615 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
617 int bytes_decoded = 0;
618 ccc = malloc(strlen(bbb) + 32768);
619 if (!strcasecmp(encoding, "base64")) {
620 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
622 else if (!strcasecmp(encoding, "quoted-printable")) {
623 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
625 ccc[bytes_decoded] = 0;
630 /* FIXME: Strip trailing whitespace */
631 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
634 bbb = (char *)realloc(bbb, 1);
644 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
649 if (!cret) return -2;
650 if (!listing) return -2;
651 if (*listing) return -2;
653 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
659 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
663 char *listing = NULL;
666 if (!cret) return -2;
668 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
669 if (ret / 100 == 1) {
672 while (*listing && strlen(listing)) {
673 extract_token(buf, listing, 0, '\n', sizeof buf);
674 remove_token(listing, 0, '\n');
676 case 0: ipc->ServInfo.pid = atoi(buf);
678 case 1: strcpy(ipc->ServInfo.nodename,buf);
680 case 2: strcpy(ipc->ServInfo.humannode,buf);
682 case 3: strcpy(ipc->ServInfo.fqdn,buf);
684 case 4: strcpy(ipc->ServInfo.software,buf);
686 case 5: ipc->ServInfo.rev_level = atoi(buf);
688 case 6: strcpy(ipc->ServInfo.site_location,buf);
690 case 7: strcpy(ipc->ServInfo.sysadm,buf);
692 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
694 case 10: ipc->ServInfo.ok_floors = atoi(buf);
696 case 11: ipc->ServInfo.paging_level = atoi(buf);
698 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
700 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
702 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
708 if (listing) free(listing);
714 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
719 if (!cret) return -2;
720 if (!listing) return -2;
721 if (*listing) return -2;
723 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
729 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
731 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
736 if (!cret) return -2;
739 sprintf(aaa, "SLRP %ld", msgnum);
742 sprintf(aaa, "SLRP HIGHEST");
744 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
750 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
755 if (!cret) return -2;
756 if (!username) return -2;
758 aaa = (char *)malloc(strlen(username) + 6);
761 sprintf(aaa, "INVT %s", username);
762 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
769 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
774 if (!cret) return -1;
775 if (!username) return -1;
777 aaa = (char *)malloc(strlen(username) + 6);
779 sprintf(aaa, "KICK %s", username);
780 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
787 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
791 if (!cret) return -2;
792 if (!qret) return -2;
793 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
794 if (!*qret) return -1;
796 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
797 if (ret / 100 == 2) {
798 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
799 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
800 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
801 qret[0]->QRflags = extract_int(cret, 3);
802 qret[0]->QRfloor = extract_int(cret, 4);
803 qret[0]->QRorder = extract_int(cret, 5);
804 qret[0]->QRdefaultview = extract_int(cret, 6);
805 qret[0]->QRflags2 = extract_int(cret, 7);
812 /* set forget to kick all users out of room */
813 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
818 if (!cret) return -2;
819 if (!qret) return -2;
821 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
822 strlen(qret->QRdirname) + 64);
825 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
826 qret->QRname, qret->QRpasswd, qret->QRdirname,
827 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
828 qret->QRdefaultview, qret->QRflags2);
829 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
836 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
838 if (!cret) return -1;
840 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
845 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
850 if (!cret) return -2;
851 if (!username) return -2;
853 aaa = (char *)malloc(strlen(username) + 6);
856 sprintf(aaa, "SETA %s", username);
857 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
864 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, const struct ctdlipcmessage *mr, char *cret)
869 if (!cret) return -2;
872 snprintf(cmd, sizeof cmd,
873 "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
874 mr->anonymous, mr->type, mr->subject, mr->author);
875 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
877 if ((flag == 0) && (subject_required != NULL)) {
878 /* Is the server strongly recommending that the user enter a message subject? */
879 if ((cret[3] != '\0') && (cret[4] != '\0')) {
880 *subject_required = extract_int(&cret[4], 1);
890 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
894 if (!cret) return -2;
895 if (!iret) return -2;
896 if (*iret) return -2;
898 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
903 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
907 if (!cret) return -2;
908 if (!msgnum) return -2;
910 sprintf(aaa, "DELE %ld", msgnum);
911 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
916 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
921 if (!cret) return -2;
922 if (!destroom) return -2;
923 if (!msgnum) return -2;
925 aaa = (char *)malloc(strlen(destroom) + 28);
928 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
929 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
936 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
940 if (!cret) return -2;
942 sprintf(aaa, "KILL %d", for_real);
943 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
948 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
949 const char *password, int floor, char *cret)
954 if (!cret) return -2;
955 if (!roomname) return -2;
958 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
960 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
963 aaa = (char *)malloc(strlen(roomname) + 40);
965 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
968 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
975 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
977 if (!cret) return -2;
979 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
984 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
990 if (!cret) return -2;
991 if (!mret) return -2;
992 if (*mret) return -2;
993 if (!message) return -2;
995 aaa = (char *)malloc(strlen(message) + 6);
998 sprintf(aaa, "MESG %s", message);
999 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1006 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1008 if (!cret) return -2;
1010 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1015 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1021 if (!cret) return -2;
1022 if (!rret) return -2;
1023 if (*rret) return -2;
1026 aaa = (char *)malloc(strlen(username) + 6);
1028 aaa = (char *)malloc(12);
1029 if (!aaa) return -1;
1032 sprintf(aaa, "GREG %s", username);
1034 sprintf(aaa, "GREG _SELF_");
1035 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1042 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1047 if (!cret) return -2;
1048 if (!username) return -2;
1049 if (axlevel < 0 || axlevel > 7) return -2;
1051 aaa = (char *)malloc(strlen(username) + 17);
1052 if (!aaa) return -1;
1054 sprintf(aaa, "VALI %s|%d", username, axlevel);
1055 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1062 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1066 if (!cret) return -1;
1067 if (!info) return -1;
1069 sprintf(aaa, "EINF %d", for_real);
1070 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1075 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1081 if (!cret) return -1;
1082 if (!listing) return -1;
1083 if (*listing) return -1;
1084 if (!searchstring) return -1;
1086 cmd = malloc(strlen(searchstring) + 10);
1087 sprintf(cmd, "LIST %s", searchstring);
1089 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1096 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1098 if (!cret) return -1;
1099 if (!info) return -1;
1101 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1107 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1111 if (!cret) return -1;
1112 if (!chek) return -1;
1114 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1115 if (ret / 100 == 2) {
1116 chek->newmail = extract_long(cret, 0);
1117 chek->needregis = extract_int(cret, 1);
1118 chek->needvalid = extract_int(cret, 2);
1125 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1130 if (!cret) return -2;
1131 if (!filename) return -2;
1133 aaa = (char *)malloc(strlen(filename) + 6);
1134 if (!aaa) return -1;
1136 sprintf(aaa, "DELF %s", filename);
1137 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1144 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1149 if (!cret) return -2;
1150 if (!filename) return -2;
1151 if (!destroom) return -2;
1153 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1154 if (!aaa) return -1;
1156 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1157 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1164 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1169 if (!cret) return -2;
1170 if (!filename) return -2;
1171 if (!destnode) return -2;
1173 aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1174 if (!aaa) return -1;
1176 sprintf(aaa, "NETF %s|%s", filename, destnode);
1177 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1184 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1189 if (!cret) return -1;
1190 if (!listing) return -1;
1191 if (*listing) return -1;
1193 *stamp = CtdlIPCServerTime(ipc, cret);
1195 *stamp = time(NULL);
1196 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1202 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1204 void (*progress_gauge_callback)
1205 (CtdlIPC*, unsigned long, unsigned long),
1214 if (!cret) return -2;
1215 if (!filename) return -2;
1216 if (!buf) return -2;
1217 if (*buf) return -2;
1218 if (ipc->downloading) return -2;
1220 aaa = (char *)malloc(strlen(filename) + 6);
1221 if (!aaa) return -1;
1223 sprintf(aaa, "OPEN %s", filename);
1224 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1226 if (ret / 100 == 2) {
1227 ipc->downloading = 1;
1228 bytes = extract_long(cret, 0);
1229 last_mod = extract_int(cret, 1);
1230 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1232 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1233 progress_gauge_callback, cret);
1235 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1236 progress_gauge_callback, cret);
1239 ret = CtdlIPCEndDownload(ipc, cret);
1241 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1242 filename, mimetype);
1249 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1251 void (*progress_gauge_callback)
1252 (CtdlIPC*, unsigned long, unsigned long),
1262 if (!cret) return -2;
1263 if (!buf) return -2;
1264 if (*buf) return -2;
1265 if (!part) return -2;
1266 if (!msgnum) return -2;
1267 if (ipc->downloading) return -2;
1269 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1270 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1271 if (ret / 100 == 2) {
1272 ipc->downloading = 1;
1273 bytes = extract_long(cret, 0);
1274 last_mod = extract_int(cret, 1);
1275 extract_token(filename, cret, 2, '|', sizeof filename);
1276 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1277 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1278 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1279 ret = CtdlIPCEndDownload(ipc, cret);
1281 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1282 filename, mimetype);
1289 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1290 void (*progress_gauge_callback)
1291 (CtdlIPC*, unsigned long, unsigned long),
1300 if (!cret) return -1;
1301 if (!buf) return -1;
1302 if (*buf) return -1;
1303 if (!filename) return -1;
1304 if (ipc->downloading) return -1;
1306 aaa = (char *)malloc(strlen(filename) + 6);
1307 if (!aaa) return -1;
1309 sprintf(aaa, "OIMG %s", filename);
1310 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1312 if (ret / 100 == 2) {
1313 ipc->downloading = 1;
1314 bytes = extract_long(cret, 0);
1315 last_mod = extract_int(cret, 1);
1316 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1317 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1318 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1319 ret = CtdlIPCEndDownload(ipc, cret);
1321 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1322 filename, mimetype);
1329 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1331 void (*progress_gauge_callback)
1332 (CtdlIPC*, unsigned long, unsigned long),
1338 if (!cret) return -1;
1339 if (!save_as) return -1;
1340 if (!comment) return -1;
1341 if (!path) return -1;
1342 if (!*path) return -1;
1343 if (ipc->uploading) return -1;
1345 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1346 if (!aaa) return -1;
1348 sprintf(aaa, "UOPN %s|%s", save_as, comment);
1349 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1351 if (ret / 100 == 2) {
1353 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1354 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1362 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1363 const char *save_as,
1364 void (*progress_gauge_callback)
1365 (CtdlIPC*, unsigned long, unsigned long),
1371 if (!cret) return -1;
1372 if (!save_as) return -1;
1373 if (!path && for_real) return -1;
1374 if (!*path && for_real) return -1;
1375 if (ipc->uploading) return -1;
1377 aaa = (char *)malloc(strlen(save_as) + 17);
1378 if (!aaa) return -1;
1380 sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1381 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1383 if (ret / 100 == 2 && for_real) {
1385 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1386 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1394 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1399 if (!cret) return -2;
1400 if (!username) return -2;
1402 aaa = (char *)malloc(strlen(username) + 6);
1403 if (!aaa) return -1;
1405 sprintf(aaa, "QUSR %s", username);
1406 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1413 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1417 if (!cret) return -2;
1418 if (!listing) return -2;
1419 if (*listing) return -2;
1421 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1426 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1431 if (!cret) return -2;
1432 if (!name) return -2;
1434 sprintf(aaa, "CFLR %s|%d", name, for_real);
1435 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1441 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1445 if (!cret) return -1;
1446 if (floornum < 0) return -1;
1448 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1449 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1454 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1459 if (!cret) return -2;
1460 if (!floorname) return -2;
1461 if (floornum < 0) return -2;
1463 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1464 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1472 * You only need to fill out hostname, the defaults will be used if any of the
1473 * other fields are not set properly.
1475 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1476 int revision, const char *software_name, const char *hostname,
1482 if (developerid < 0 || clientid < 0 || revision < 0 ||
1486 revision = REV_LEVEL - 600;
1487 software_name = "Citadel (libcitadel)";
1489 if (!hostname) return -2;
1491 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1492 if (!aaa) return -1;
1494 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1495 revision, software_name, hostname);
1496 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1503 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1509 if (!cret) return -2;
1510 if (!username) return -2;
1512 aaa = (char *)malloc(strlen(username) + 8);
1513 if (!aaa) return -1;
1516 sprintf(aaa, "SEXP %s|-", username);
1517 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1520 sprintf(aaa, "SEXP %s||", username);
1521 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1529 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1533 if (!cret) return -2;
1534 if (!listing) return -2;
1535 if (*listing) return -2;
1537 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1542 /* mode is 0 = enable, 1 = disable, 2 = status */
1543 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1547 if (!cret) return -2;
1549 sprintf(aaa, "DEXP %d", mode);
1550 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1555 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1557 if (!cret) return -2;
1558 if (!bio) return -2;
1560 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1566 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1572 if (!cret) return -2;
1573 if (!username) return -2;
1574 if (!listing) return -2;
1575 if (*listing) return -2;
1577 aaa = (char *)malloc(strlen(username) + 6);
1578 if (!aaa) return -1;
1580 sprintf(aaa, "RBIO %s", username);
1581 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1588 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1592 if (!cret) return -2;
1593 if (!listing) return -2;
1594 if (*listing) return -2;
1596 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1601 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1605 if (!cret) return -1;
1607 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1608 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1613 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1617 if (!cret) return -1;
1619 sprintf(aaa, "TERM %d", sid);
1620 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1625 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1627 if (!cret) return -1;
1629 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1634 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1638 if (!cret) return -1;
1640 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1641 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1646 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1652 if (!cret) return -2;
1653 if (!text) return -2;
1654 if (!filename) return -2;
1656 aaa = (char *)malloc(strlen(filename) + 6);
1657 if (!aaa) return -1;
1659 sprintf(aaa, "EMSG %s", filename);
1660 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1667 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1672 if (!cret) return -2;
1673 if (!hostname) return -2;
1675 aaa = (char *)malloc(strlen(hostname) + 6);
1676 if (!aaa) return -1;
1678 sprintf(aaa, "HCHG %s", hostname);
1679 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1686 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1691 if (!cret) return -2;
1692 if (!roomname) return -2;
1694 aaa = (char *)malloc(strlen(roomname) + 6);
1695 if (!aaa) return -1;
1697 sprintf(aaa, "RCHG %s", roomname);
1698 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1705 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1710 if (!cret) return -2;
1711 if (!username) return -2;
1713 aaa = (char *)malloc(strlen(username) + 6);
1714 if (!aaa) return -1;
1716 sprintf(aaa, "UCHG %s", username);
1717 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1724 /* This function returns the actual server time reported, or 0 if error */
1725 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1727 register time_t tret;
1730 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1731 if (ret / 100 == 2) {
1732 tret = extract_long(cret, 0);
1741 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1742 struct ctdluser **uret, char *cret)
1747 if (!cret) return -2;
1748 if (!uret) return -2;
1749 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1750 if (!*uret) return -1;
1752 sprintf(aaa, "AGUP %s", who);
1753 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1755 if (ret / 100 == 2) {
1756 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1757 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1758 uret[0]->flags = extract_int(cret, 2);
1759 uret[0]->timescalled = extract_long(cret, 3);
1760 uret[0]->posted = extract_long(cret, 4);
1761 uret[0]->axlevel = extract_int(cret, 5);
1762 uret[0]->usernum = extract_long(cret, 6);
1763 uret[0]->lastcall = extract_long(cret, 7);
1764 uret[0]->USuserpurge = extract_int(cret, 8);
1771 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1776 if (!cret) return -2;
1777 if (!uret) return -2;
1779 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1780 if (!aaa) return -1;
1782 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1783 uret->fullname, uret->password, uret->flags,
1784 uret->timescalled, uret->posted, uret->axlevel,
1785 uret->usernum, uret->lastcall, uret->USuserpurge);
1786 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1793 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1794 /* caller must free the struct ExpirePolicy */
1795 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1796 struct ExpirePolicy **policy, char *cret)
1798 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1802 if (!cret) return -2;
1803 if (!policy) return -2;
1804 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1805 if (!*policy) return -1;
1806 if (which < 0 || which > 3) return -2;
1808 sprintf(cmd, "GPEX %s", proto[which]);
1809 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1810 if (ret / 100 == 2) {
1811 policy[0]->expire_mode = extract_int(cret, 0);
1812 policy[0]->expire_value = extract_int(cret, 1);
1819 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1820 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1821 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1822 struct ExpirePolicy *policy, char *cret)
1825 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1827 if (!cret) return -2;
1828 if (which < 0 || which > 3) return -2;
1829 if (!policy) return -2;
1830 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1831 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1833 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1834 policy->expire_mode, policy->expire_value);
1835 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1840 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1844 if (!cret) return -2;
1845 if (!listing) return -2;
1846 if (*listing) return -2;
1848 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1849 listing, &bytes, cret);
1854 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1856 if (!cret) return -2;
1857 if (!listing) return -2;
1859 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1865 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1866 char **listing, char *cret)
1872 if (!cret) return -2;
1873 if (!mimetype) return -2;
1874 if (!listing) return -2;
1875 if (*listing) return -2;
1877 aaa = malloc(strlen(mimetype) + 13);
1878 if (!aaa) return -1;
1879 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1880 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1881 listing, &bytes, cret);
1888 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1889 const char *listing, char *cret)
1894 if (!cret) return -2;
1895 if (!mimetype) return -2;
1896 if (!listing) return -2;
1898 aaa = malloc(strlen(mimetype) + 13);
1899 if (!aaa) return -1;
1900 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1901 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1909 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1913 if (!cret) return -2;
1914 if (!listing) return -2;
1915 if (*listing) return -2;
1917 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1918 listing, &bytes, cret);
1923 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1925 if (!cret) return -2;
1926 if (!listing) return -2;
1928 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1934 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1938 if (!cret) return -2;
1939 if (session < 0) return -2;
1941 sprintf(aaa, "REQT %d", session);
1942 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1947 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1951 if (!cret) return -2;
1952 if (msgnum < 0) return -2;
1954 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1955 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1960 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1969 /* New SSL object */
1970 temp_ssl = SSL_new(ssl_ctx);
1972 error_printf("SSL_new failed: %s\n",
1973 ERR_reason_error_string(ERR_get_error()));
1976 /* Pointless flag waving */
1977 #if SSLEAY_VERSION_NUMBER >= 0x0922
1978 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1981 if (!access(EGD_POOL, F_OK))
1984 if (!RAND_status()) {
1985 error_printf("PRNG not properly seeded\n");
1989 /* Associate network connection with SSL object */
1990 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1991 error_printf("SSL_set_fd failed: %s\n",
1992 ERR_reason_error_string(ERR_get_error()));
1996 if (status_hook != NULL)
1997 status_hook("Requesting encryption...\r");
1999 /* Ready to start SSL/TLS */
2001 CtdlIPC_putline(ipc, "STLS");
2002 CtdlIPC_getline(ipc, buf);
2003 if (buf[0] != '2') {
2004 error_printf("Server can't start TLS: %s\n", buf);
2008 r = CtdlIPCGenericCommand(ipc,
2009 "STLS", NULL, 0, NULL, NULL, cret);
2011 error_printf("Server can't start TLS: %s\n", buf);
2016 /* Do SSL/TLS handshake */
2017 if ((a = SSL_connect(temp_ssl)) < 1) {
2018 error_printf("SSL_connect failed: %s\n",
2019 ERR_reason_error_string(ERR_get_error()));
2023 ipc->ssl = temp_ssl;
2025 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2029 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2030 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2031 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2032 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2038 #endif /* HAVE_OPENSSL */
2043 static void endtls(SSL *ssl)
2054 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2059 if (!address) return -2;
2060 if (!cret) return -2;
2062 aaa = (char *)malloc(strlen(address) + 6);
2063 if (!aaa) return -1;
2065 sprintf(aaa, "QDIR %s", address);
2066 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2073 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2077 if (!cret) return -2;
2078 sprintf(aaa, "IPGM %d", secret);
2079 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2084 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2088 if (!cret) return -2;
2089 if (!mret) return -2;
2090 if (*mret) return -2;
2092 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2113 /* ************************************************************************** */
2114 /* Stuff below this line is not for public consumption */
2115 /* ************************************************************************** */
2118 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2120 if (ipc->network_status_cb) ipc->network_status_cb(1);
2121 #ifdef THREADED_CLIENT
2122 pthread_mutex_lock(&(ipc->mutex));
2127 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2129 #ifdef THREADED_CLIENT
2130 pthread_mutex_unlock(&(ipc->mutex));
2132 if (ipc->network_status_cb) ipc->network_status_cb(0);
2136 /* Read a listing from the server up to 000. Append to dest if it exists */
2137 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2146 length = strlen(ret);
2151 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2152 linelength = strlen(aaa);
2153 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2155 strcpy(&ret[length], aaa);
2156 length += linelength;
2157 strcpy(&ret[length++], "\n");
2165 /* Send a listing to the server; generate the ending 000. */
2166 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2170 text = (char *)malloc(strlen(listing) + 6);
2172 strcpy(text, listing);
2173 while (text[strlen(text) - 1] == '\n')
2174 text[strlen(text) - 1] = '\0';
2175 strcat(text, "\n000");
2176 CtdlIPC_putline(ipc, text);
2180 /* Malloc failed but we are committed to send */
2181 /* This may result in extra blanks at the bottom */
2182 CtdlIPC_putline(ipc, text);
2183 CtdlIPC_putline(ipc, "000");
2189 /* Partial read of file from server */
2190 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2192 register size_t len = 0;
2196 if (!cret) return 0;
2197 if (bytes < 1) return 0;
2200 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2201 CtdlIPC_putline(ipc, aaa);
2202 CtdlIPC_getline(ipc, aaa);
2204 strcpy(cret, &aaa[4]);
2206 len = extract_long(&aaa[4], 0);
2207 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2209 /* I know what I'm doing */
2210 serv_read(ipc, ((char *)(*buf) + offset), len);
2212 /* We have to read regardless */
2213 serv_read(ipc, aaa, len);
2217 CtdlIPC_unlock(ipc);
2223 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2227 if (!cret) return -2;
2228 if (!ipc->downloading) return -2;
2230 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2232 ipc->downloading = 0;
2238 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2242 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2243 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2250 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2251 void (*progress_gauge_callback)
2252 (CtdlIPC*, unsigned long, unsigned long),
2255 register size_t len;
2257 if (!cret) return -1;
2258 if (!buf) return -1;
2259 if (*buf) return -1;
2260 if (!ipc->downloading) return -1;
2263 if (progress_gauge_callback)
2264 progress_gauge_callback(ipc, len, bytes);
2265 while (len < bytes) {
2266 register size_t block;
2268 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2274 if (progress_gauge_callback)
2275 progress_gauge_callback(ipc, len, bytes);
2280 /* READ - pipelined */
2281 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2283 void (*progress_gauge_callback)
2284 (CtdlIPC*, unsigned long, unsigned long),
2287 register size_t len;
2288 register int calls; /* How many calls in the pipeline */
2289 register int i; /* iterator */
2292 if (!cret) return -1;
2293 if (!buf) return -1;
2294 if (*buf) return -1;
2295 if (!ipc->downloading) return -1;
2297 *buf = (void *)realloc(*buf, bytes - resume);
2298 if (!*buf) return -1;
2302 if (progress_gauge_callback)
2303 progress_gauge_callback(ipc, len, bytes);
2305 /* How many calls will be in the pipeline? */
2306 calls = (bytes - resume) / 4096;
2307 if ((bytes - resume) % 4096) calls++;
2309 /* Send all requests at once */
2310 for (i = 0; i < calls; i++) {
2311 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2312 CtdlIPC_putline(ipc, aaa);
2315 /* Receive all responses at once */
2316 for (i = 0; i < calls; i++) {
2317 CtdlIPC_getline(ipc, aaa);
2319 strcpy(cret, &aaa[4]);
2321 len = extract_long(&aaa[4], 0);
2322 /* I know what I'm doing */
2323 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2325 if (progress_gauge_callback)
2326 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2328 CtdlIPC_unlock(ipc);
2334 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2339 if (!cret) return -1;
2340 if (!ipc->uploading) return -1;
2342 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2343 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2350 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2351 void (*progress_gauge_callback)
2352 (CtdlIPC*, unsigned long, unsigned long),
2355 register int ret = -1;
2356 register size_t offset = 0;
2363 if (!cret) return -1;
2364 if (!path) return -1;
2365 if (!*path) return -1;
2367 fd = fopen(path, "r");
2370 fseek(fd, 0L, SEEK_END);
2374 if (progress_gauge_callback)
2375 progress_gauge_callback(ipc, 0, bytes);
2377 while (offset < bytes) {
2378 register size_t to_write;
2380 /* Read some data in */
2381 to_write = fread(buf, 1, 4096, fd);
2383 if (feof(fd) || ferror(fd)) break;
2385 sprintf(aaa, "WRIT %d", (int)to_write);
2386 CtdlIPC_putline(ipc, aaa);
2387 CtdlIPC_getline(ipc, aaa);
2388 strcpy(cret, &aaa[4]);
2390 if (aaa[0] == '7') {
2391 to_write = extract_long(&aaa[4], 0);
2393 serv_write(ipc, buf, to_write);
2395 if (progress_gauge_callback)
2396 progress_gauge_callback(ipc, offset, bytes);
2397 /* Detect short reads and back up if needed */
2398 /* offset will never be negative anyway */
2399 fseek(fd, (signed)offset, SEEK_SET);
2404 if (progress_gauge_callback)
2405 progress_gauge_callback(ipc, 1, 1);
2408 return (!ferr ? ret : -2);
2413 * Generic command method. This method should handle any server command
2414 * except for CHAT. It takes the following arguments:
2416 * ipc The server to speak with
2417 * command Preformatted command to send to server
2418 * to_send A text or binary file to send to server
2419 * (only sent if server requests it)
2420 * bytes_to_send The number of bytes in to_send (required if
2421 * sending binary, optional if sending listing)
2422 * to_receive Pointer to a NULL pointer, if the server
2423 * sends text or binary we will allocate memory
2424 * for the file and stuff it here
2425 * bytes_to_receive If a file is received, we will store its
2427 * proto_response The protocol response. Caller must provide
2428 * this buffer and ensure that it is at least
2429 * 128 bytes in length.
2431 * This function returns a number equal to the protocol response number,
2432 * -1 if an internal error occurred, -2 if caller provided bad values,
2433 * or 0 - the protocol response number if bad values were found during
2434 * the protocol exchange.
2435 * It stores the protocol response string (minus the number) in
2436 * protocol_response as described above. Some commands send additional
2437 * data in this string.
2439 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2440 const char *command, const char *to_send,
2441 size_t bytes_to_send, char **to_receive,
2442 size_t *bytes_to_receive, char *proto_response)
2448 if (!command) return -2;
2449 if (!proto_response) return -2;
2452 if (ipc->ssl) watch_ssl = 1;
2456 CtdlIPC_putline(ipc, command);
2458 CtdlIPC_getline(ipc, proto_response);
2459 if (proto_response[3] == '*')
2461 ret = atoi(proto_response);
2462 strcpy(proto_response, &proto_response[4]);
2463 switch (ret / 100) {
2464 default: /* Unknown, punt */
2466 case 3: /* MORE_DATA */
2468 /* Don't need to do anything */
2470 case 1: /* LISTING_FOLLOWS */
2471 if (to_receive && !*to_receive && bytes_to_receive) {
2472 *to_receive = CtdlIPCReadListing(ipc, NULL);
2473 } else { /* Drain */
2474 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2478 case 4: /* SEND_LISTING */
2480 CtdlIPCSendListing(ipc, to_send);
2482 /* No listing given, fake it */
2483 CtdlIPC_putline(ipc, "000");
2487 case 6: /* BINARY_FOLLOWS */
2488 if (to_receive && !*to_receive && bytes_to_receive) {
2490 extract_long(proto_response, 0);
2491 *to_receive = (char *)
2492 malloc((size_t)*bytes_to_receive);
2496 serv_read(ipc, *to_receive,
2503 drain = extract_long(proto_response, 0);
2504 while (drain > SIZ) {
2505 serv_read(ipc, buf, SIZ);
2508 serv_read(ipc, buf, drain);
2512 case 7: /* SEND_BINARY */
2513 if (to_send && bytes_to_send) {
2514 serv_write(ipc, to_send, bytes_to_send);
2515 } else if (bytes_to_send) {
2516 /* Fake it, send nulls */
2519 fake = bytes_to_send;
2520 memset(buf, '\0', SIZ);
2521 while (fake > SIZ) {
2522 serv_write(ipc, buf, SIZ);
2525 serv_write(ipc, buf, fake);
2527 } /* else who knows? DANGER WILL ROBINSON */
2529 case 8: /* START_CHAT_MODE */
2530 if (!strncasecmp(command, "CHAT", 4)) {
2531 /* Don't call chatmode with generic! */
2532 CtdlIPC_putline(ipc, "/quit");
2535 /* In this mode we send then receive listing */
2537 CtdlIPCSendListing(ipc, to_send);
2539 /* No listing given, fake it */
2540 CtdlIPC_putline(ipc, "000");
2543 if (to_receive && !*to_receive
2544 && bytes_to_receive) {
2545 *to_receive = CtdlIPCReadListing(ipc, NULL);
2546 } else { /* Drain */
2547 while (CtdlIPC_getline(ipc, buf),
2548 strcmp(buf, "000")) ;
2553 case 9: /* ASYNC_MSG */
2554 /* CtdlIPCDoAsync(ret, proto_response); */
2555 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2561 CtdlIPC_unlock(ipc);
2566 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2568 struct hostent *phe;
2569 struct servent *pse;
2570 struct protoent *ppe;
2571 struct sockaddr_in sin;
2574 memset(&sin, 0, sizeof(sin));
2575 sin.sin_family = AF_INET;
2577 pse = getservbyname(service, protocol);
2579 sin.sin_port = pse->s_port;
2581 else if (atoi(service) > 0) {
2582 sin.sin_port = htons(atoi(service));
2585 sin.sin_port = htons(defaultPort);
2587 phe = gethostbyname(host);
2589 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2590 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2593 if ((ppe = getprotobyname(protocol)) == 0) {
2596 if (!strcmp(protocol, "udp")) {
2602 s = socket(PF_INET, type, ppe->p_proto);
2607 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2615 static int uds_connectsock(int *isLocal, char *sockpath)
2617 struct sockaddr_un addr;
2620 memset(&addr, 0, sizeof(addr));
2621 addr.sun_family = AF_UNIX;
2622 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2624 s = socket(AF_UNIX, SOCK_STREAM, 0);
2629 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2640 * input binary data from socket
2642 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2644 unsigned int len, rlen;
2646 #if defined(HAVE_OPENSSL)
2648 serv_read_ssl(ipc, buf, bytes);
2653 while (len < bytes) {
2654 rlen = read(ipc->sock, &buf[len], bytes - len);
2656 connection_died(ipc, 0);
2665 * send binary to server
2667 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2669 unsigned int bytes_written = 0;
2672 #if defined(HAVE_OPENSSL)
2674 serv_write_ssl(ipc, buf, nbytes);
2678 while (bytes_written < nbytes) {
2679 retval = write(ipc->sock, &buf[bytes_written],
2680 nbytes - bytes_written);
2682 connection_died(ipc, 0);
2685 bytes_written += retval;
2692 * input binary data from encrypted connection
2694 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2700 while (len < bytes) {
2701 if (SSL_want_read(ipc->ssl)) {
2702 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2703 error_printf("SSL_write in serv_read:\n");
2704 ERR_print_errors_fp(stderr);
2707 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2711 errval = SSL_get_error(ipc->ssl, rlen);
2712 if (errval == SSL_ERROR_WANT_READ ||
2713 errval == SSL_ERROR_WANT_WRITE) {
2718 Not sure why we'd want to handle these error codes any differently,
2719 but this definitely isn't the way to handle them. Someone must have
2720 naively assumed that we could fall back to unencrypted communications,
2721 but all it does is just recursively blow the stack.
2722 if (errval == SSL_ERROR_ZERO_RETURN ||
2723 errval == SSL_ERROR_SSL) {
2724 serv_read(ipc, &buf[len], bytes - len);
2728 error_printf("SSL_read in serv_read: %s\n",
2729 ERR_reason_error_string(ERR_peek_error()));
2730 connection_died(ipc, 1);
2739 * send binary to server encrypted
2741 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2743 unsigned int bytes_written = 0;
2747 while (bytes_written < nbytes) {
2748 if (SSL_want_write(ipc->ssl)) {
2749 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2750 error_printf("SSL_read in serv_write:\n");
2751 ERR_print_errors_fp(stderr);
2754 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2755 nbytes - bytes_written);
2759 errval = SSL_get_error(ipc->ssl, retval);
2760 if (errval == SSL_ERROR_WANT_READ ||
2761 errval == SSL_ERROR_WANT_WRITE) {
2765 if (errval == SSL_ERROR_ZERO_RETURN ||
2766 errval == SSL_ERROR_SSL) {
2767 serv_write(ipc, &buf[bytes_written],
2768 nbytes - bytes_written);
2771 error_printf("SSL_write in serv_write: %s\n",
2772 ERR_reason_error_string(ERR_peek_error()));
2773 connection_died(ipc, 1);
2776 bytes_written += retval;
2781 #ifdef THREADED_CLIENT
2782 static void ssl_lock(int mode, int n, const char *file, int line)
2784 if (mode & CRYPTO_LOCK)
2785 pthread_mutex_lock(Critters[n]);
2787 pthread_mutex_unlock(Critters[n]);
2789 #endif /* THREADED_CLIENT */
2792 static void CtdlIPC_init_OpenSSL(void)
2795 SSL_METHOD *ssl_method;
2798 /* already done init */
2807 SSL_load_error_strings();
2808 SSLeay_add_ssl_algorithms();
2810 /* Set up the SSL context in which we will oeprate */
2811 ssl_method = SSLv23_client_method();
2812 ssl_ctx = SSL_CTX_new(ssl_method);
2814 error_printf("SSL_CTX_new failed: %s\n",
2815 ERR_reason_error_string(ERR_get_error()));
2818 /* Any reasonable cipher we can get */
2819 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2820 error_printf("No ciphers available for encryption\n");
2823 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2825 /* Load DH parameters into the context */
2828 error_printf("Can't allocate a DH object: %s\n",
2829 ERR_reason_error_string(ERR_get_error()));
2832 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2833 error_printf("Can't assign DH_P: %s\n",
2834 ERR_reason_error_string(ERR_get_error()));
2838 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2839 error_printf("Can't assign DH_G: %s\n",
2840 ERR_reason_error_string(ERR_get_error()));
2845 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2848 #ifdef THREADED_CLIENT
2849 /* OpenSSL requires callbacks for threaded clients */
2850 CRYPTO_set_locking_callback(ssl_lock);
2851 CRYPTO_set_id_callback(id_callback);
2853 /* OpenSSL requires us to do semaphores for threaded clients */
2854 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2856 perror("malloc failed");
2859 for (a = 0; a < CRYPTO_num_locks(); a++) {
2860 Critters[a] = malloc(sizeof (pthread_mutex_t));
2862 perror("malloc failed");
2865 pthread_mutex_init(Critters[a], NULL);
2868 #endif /* THREADED_CLIENT */
2873 #ifdef THREADED_CLIENT
2874 static unsigned long id_callback(void) {
2875 return (unsigned long)pthread_self();
2877 #endif /* THREADED_CLIENT */
2878 #endif /* HAVE_OPENSSL */
2882 ReadNetworkChunk(CtdlIPC* ipc)
2899 FD_SET(ipc->sock, &read_fd);
2900 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2902 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2906 *(ipc->BufPtr) = '\0';
2907 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2908 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2910 ipc->BufPtr[n]='\0';
2918 if (!(errno == EINTR || errno == EAGAIN))
2919 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2925 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2927 ipc->BufPtr[n]='\0';
2932 connection_died(ipc, 0);
2940 * input string from socket - implemented in terms of serv_read()
2944 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2947 char *aptr, *bptr, *aeptr, *beptr;
2949 // error_printf("---\n");
2952 #if defined(HAVE_OPENSSL)
2955 /* Read one character at a time. */
2957 serv_read(ipc, &buf[i], 1);
2958 if (buf[i] == '\n' || i == (SIZ-1))
2962 /* If we got a long line, discard characters until the newline. */
2964 while (buf[i] != '\n')
2965 serv_read(ipc, &buf[i], 1);
2967 /* Strip the trailing newline (and carriage return, if present) */
2968 if (i>=0 && buf[i] == 10) buf[i--] = 0;
2969 if (i>=0 && buf[i] == 13) buf[i--] = 0;
2974 if (ipc->Buf == NULL)
2977 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
2979 ipc->BufPtr = ipc->Buf;
2983 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
2984 if (ipc->BufUsed == 0)
2985 ReadNetworkChunk(ipc);
2987 //// if (ipc->BufUsed != 0) while (1)
2993 aeptr = ipc->Buf + ipc->BufSize;
2994 while ((aptr < aeptr) &&
2998 *(bptr++) = *(aptr++);
2999 if ((*aptr == '\n') && (aptr < aeptr))
3001 /* Terminate it right, remove the line breaks */
3002 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3004 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3007 // fprintf(stderr, "parsing %d %d %d - %d %d %d %s\n", ipc->BufPtr - ipc->Buf, aptr - ipc->BufPtr, ipc->BufUsed , *aptr, *(aptr-1), *(aptr+1), buf);
3008 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3011 /* is there more in the buffer we need to read later? */
3012 if (ipc->Buf + ipc->BufUsed > aptr)
3019 ipc->BufPtr = ipc->Buf;
3021 // error_printf("----bla6\n");
3024 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3025 else if ((ipc->BufPtr != ipc->Buf) &&
3026 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3028 size_t NewBufSize = ipc->BufSize * 2;
3029 int delta = (ipc->BufPtr - ipc->Buf);
3032 /* if the line would end after our buffer, we should use a bigger buffer. */
3033 NewBuf = (char *)malloc (NewBufSize + 10);
3034 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3036 ipc->Buf = ipc->BufPtr = NewBuf;
3037 ipc->BufUsed -= delta;
3038 ipc->BufSize = NewBufSize;
3040 if (ReadNetworkChunk(ipc) <0)
3042 // error_printf("----bla\n");
3046 /// error_printf("----bl45761%s\nipc->BufUsed");
3048 // error_printf("----bla1\n");
3051 #else /* CHUNKED_READ */
3053 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3057 /* Read one character at a time. */
3059 serv_read(ipc, &buf[i], 1);
3060 if (buf[i] == '\n' || i == (SIZ-1))
3064 /* If we got a long line, discard characters until the newline. */
3066 while (buf[i] != '\n')
3067 serv_read(ipc, &buf[i], 1);
3069 /* Strip the trailing newline (and carriage return, if present) */
3070 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3071 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3075 #endif /* CHUNKED_READ */
3078 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3080 CtdlIPC_getline(ipc, buf);
3084 * send line to server - implemented in terms of serv_write()
3086 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3092 cmd = malloc(len + 2);
3094 /* This requires no extra memory */
3095 serv_write(ipc, buf, len);
3096 serv_write(ipc, "\n", 1);
3098 /* This is network-optimized */
3099 strncpy(cmd, buf, len);
3100 strcpy(cmd + len, "\n");
3101 serv_write(ipc, cmd, len + 1);
3105 ipc->last_command_sent = time(NULL);
3108 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3110 CtdlIPC_putline(ipc, buf);
3117 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3125 ipc = ialloc(CtdlIPC);
3129 #if defined(HAVE_OPENSSL)
3131 CtdlIPC_init_OpenSSL();
3133 #if defined(HAVE_PTHREAD_H)
3134 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3136 ipc->sock = -1; /* Not connected */
3137 ipc->isLocal = 0; /* Not local, of course! */
3138 ipc->downloading = 0;
3140 ipc->last_command_sent = 0L;
3141 ipc->network_status_cb = NULL;
3146 strcpy(cithost, DEFAULT_HOST); /* default host */
3147 strcpy(citport, DEFAULT_PORT); /* default port */
3149 /* Allow caller to supply our values (Windows) */
3150 if (hostbuf && strlen(hostbuf) > 0)
3151 strcpy(cithost, hostbuf);
3152 if (portbuf && strlen(portbuf) > 0)
3153 strcpy(citport, portbuf);
3155 /* Read host/port from command line if present */
3156 for (a = 0; a < argc; ++a) {
3159 } else if (a == 1) {
3160 strcpy(cithost, argv[a]);
3161 } else if (a == 2) {
3162 strcpy(citport, argv[a]);
3164 error_printf("%s: usage: ",argv[0]);
3165 error_printf("%s [host] [port] ",argv[0]);
3172 if ((!strcmp(cithost, "localhost"))
3173 || (!strcmp(cithost, "127.0.0.1"))) {
3177 /* If we're using a unix domain socket we can do a bunch of stuff */
3178 if (!strcmp(cithost, UDS)) {
3179 if (!strcasecmp(citport, DEFAULT_PORT)) {
3180 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3183 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3185 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3186 if (ipc->sock == -1) {
3190 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3191 if (portbuf != NULL) strcpy(portbuf, sockpath);
3195 ipc->sock = connectsock(cithost, citport, "tcp", 504);
3196 if (ipc->sock == -1) {
3200 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3201 if (portbuf != NULL) strcpy(portbuf, citport);
3207 * Disconnect and delete the IPC class (destructor)
3209 void CtdlIPC_delete(CtdlIPC* ipc)
3213 SSL_shutdown(ipc->ssl);
3218 if (ipc->sock > -1) {
3219 shutdown(ipc->sock, 2); /* Close it up */
3222 if (ipc->Buf != NULL)
3231 * Disconnect and delete the IPC class (destructor)
3232 * Also NULLs out the pointer
3234 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3236 CtdlIPC_delete(*pipc);
3242 * return the file descriptor of the server socket so we can select() on it.
3244 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3247 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3254 * return one character
3256 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3259 char CtdlIPC_get(CtdlIPC* ipc)
3264 serv_read(ipc, buf, 1);