16 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <netinet/in.h>
29 #ifdef THREADED_CLIENT
32 #include <libcitadel.h>
34 #include "citadel_ipc.h"
35 #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 char MimeTestBuf[64];
1339 const char *MimeType;
1342 if (!cret) return -1;
1343 if (!save_as) return -1;
1344 if (!comment) return -1;
1345 if (!path) return -1;
1346 if (!*path) return -1;
1347 if (ipc->uploading) return -1;
1349 uploadFP = fopen(path, "r");
1350 if (!uploadFP) return -2;
1352 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1357 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1358 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1359 if (!aaa) return -1;
1361 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1362 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1364 if (ret / 100 == 2) {
1366 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1367 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1375 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1376 const char *save_as,
1377 void (*progress_gauge_callback)
1378 (CtdlIPC*, unsigned long, unsigned long),
1384 char MimeTestBuf[64];
1385 const char *MimeType;
1388 if (!cret) return -1;
1389 if (!save_as) return -1;
1390 if (!path && for_real) return -1;
1391 if (!*path && for_real) return -1;
1392 if (ipc->uploading) return -1;
1394 aaa = (char *)malloc(strlen(save_as) + 17);
1395 if (!aaa) return -1;
1397 uploadFP = fopen(path, "r");
1398 if (!uploadFP) return -2;
1400 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1404 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1406 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1407 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1409 if (ret / 100 == 2 && for_real) {
1411 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1412 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1420 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1425 if (!cret) return -2;
1426 if (!username) return -2;
1428 aaa = (char *)malloc(strlen(username) + 6);
1429 if (!aaa) return -1;
1431 sprintf(aaa, "QUSR %s", username);
1432 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1439 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1443 if (!cret) return -2;
1444 if (!listing) return -2;
1445 if (*listing) return -2;
1447 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1452 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1457 if (!cret) return -2;
1458 if (!name) return -2;
1460 sprintf(aaa, "CFLR %s|%d", name, for_real);
1461 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1467 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1471 if (!cret) return -1;
1472 if (floornum < 0) return -1;
1474 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1475 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1480 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1485 if (!cret) return -2;
1486 if (!floorname) return -2;
1487 if (floornum < 0) return -2;
1489 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1490 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1498 * You only need to fill out hostname, the defaults will be used if any of the
1499 * other fields are not set properly.
1501 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1502 int revision, const char *software_name, const char *hostname,
1508 if (developerid < 0 || clientid < 0 || revision < 0 ||
1512 revision = REV_LEVEL - 600;
1513 software_name = "Citadel (libcitadel)";
1515 if (!hostname) return -2;
1517 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1518 if (!aaa) return -1;
1520 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1521 revision, software_name, hostname);
1522 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1529 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1535 if (!cret) return -2;
1536 if (!username) return -2;
1538 aaa = (char *)malloc(strlen(username) + 8);
1539 if (!aaa) return -1;
1542 sprintf(aaa, "SEXP %s|-", username);
1543 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1546 sprintf(aaa, "SEXP %s||", username);
1547 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1555 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1559 if (!cret) return -2;
1560 if (!listing) return -2;
1561 if (*listing) return -2;
1563 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1568 /* mode is 0 = enable, 1 = disable, 2 = status */
1569 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1573 if (!cret) return -2;
1575 sprintf(aaa, "DEXP %d", mode);
1576 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1581 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1583 if (!cret) return -2;
1584 if (!bio) return -2;
1586 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1592 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1598 if (!cret) return -2;
1599 if (!username) return -2;
1600 if (!listing) return -2;
1601 if (*listing) return -2;
1603 aaa = (char *)malloc(strlen(username) + 6);
1604 if (!aaa) return -1;
1606 sprintf(aaa, "RBIO %s", username);
1607 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1614 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1618 if (!cret) return -2;
1619 if (!listing) return -2;
1620 if (*listing) return -2;
1622 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1627 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1631 if (!cret) return -1;
1633 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1634 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1639 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1643 if (!cret) return -1;
1645 sprintf(aaa, "TERM %d", sid);
1646 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1651 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1653 if (!cret) return -1;
1655 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1660 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1664 if (!cret) return -1;
1666 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1667 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1672 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1678 if (!cret) return -2;
1679 if (!text) return -2;
1680 if (!filename) return -2;
1682 aaa = (char *)malloc(strlen(filename) + 6);
1683 if (!aaa) return -1;
1685 sprintf(aaa, "EMSG %s", filename);
1686 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1693 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1698 if (!cret) return -2;
1699 if (!hostname) return -2;
1701 aaa = (char *)malloc(strlen(hostname) + 6);
1702 if (!aaa) return -1;
1704 sprintf(aaa, "HCHG %s", hostname);
1705 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1712 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1717 if (!cret) return -2;
1718 if (!roomname) return -2;
1720 aaa = (char *)malloc(strlen(roomname) + 6);
1721 if (!aaa) return -1;
1723 sprintf(aaa, "RCHG %s", roomname);
1724 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1731 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1736 if (!cret) return -2;
1737 if (!username) return -2;
1739 aaa = (char *)malloc(strlen(username) + 6);
1740 if (!aaa) return -1;
1742 sprintf(aaa, "UCHG %s", username);
1743 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1750 /* This function returns the actual server time reported, or 0 if error */
1751 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1753 register time_t tret;
1756 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1757 if (ret / 100 == 2) {
1758 tret = extract_long(cret, 0);
1767 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1768 struct ctdluser **uret, char *cret)
1773 if (!cret) return -2;
1774 if (!uret) return -2;
1775 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1776 if (!*uret) return -1;
1778 sprintf(aaa, "AGUP %s", who);
1779 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1781 if (ret / 100 == 2) {
1782 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1783 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1784 uret[0]->flags = extract_int(cret, 2);
1785 uret[0]->timescalled = extract_long(cret, 3);
1786 uret[0]->posted = extract_long(cret, 4);
1787 uret[0]->axlevel = extract_int(cret, 5);
1788 uret[0]->usernum = extract_long(cret, 6);
1789 uret[0]->lastcall = extract_long(cret, 7);
1790 uret[0]->USuserpurge = extract_int(cret, 8);
1797 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1802 if (!cret) return -2;
1803 if (!uret) return -2;
1805 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1806 if (!aaa) return -1;
1808 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1809 uret->fullname, uret->password, uret->flags,
1810 uret->timescalled, uret->posted, uret->axlevel,
1811 uret->usernum, uret->lastcall, uret->USuserpurge);
1812 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1819 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1820 /* caller must free the struct ExpirePolicy */
1821 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1822 struct ExpirePolicy **policy, char *cret)
1824 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1828 if (!cret) return -2;
1829 if (!policy) return -2;
1830 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1831 if (!*policy) return -1;
1832 if (which < 0 || which > 3) return -2;
1834 sprintf(cmd, "GPEX %s", proto[which]);
1835 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1836 if (ret / 100 == 2) {
1837 policy[0]->expire_mode = extract_int(cret, 0);
1838 policy[0]->expire_value = extract_int(cret, 1);
1845 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1846 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1847 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1848 struct ExpirePolicy *policy, char *cret)
1851 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1853 if (!cret) return -2;
1854 if (which < 0 || which > 3) return -2;
1855 if (!policy) return -2;
1856 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1857 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1859 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1860 policy->expire_mode, policy->expire_value);
1861 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1866 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1870 if (!cret) return -2;
1871 if (!listing) return -2;
1872 if (*listing) return -2;
1874 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1875 listing, &bytes, cret);
1880 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1882 if (!cret) return -2;
1883 if (!listing) return -2;
1885 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1891 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1892 char **listing, char *cret)
1898 if (!cret) return -2;
1899 if (!mimetype) return -2;
1900 if (!listing) return -2;
1901 if (*listing) return -2;
1903 aaa = malloc(strlen(mimetype) + 13);
1904 if (!aaa) return -1;
1905 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1906 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1907 listing, &bytes, cret);
1914 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1915 const char *listing, char *cret)
1920 if (!cret) return -2;
1921 if (!mimetype) return -2;
1922 if (!listing) return -2;
1924 aaa = malloc(strlen(mimetype) + 13);
1925 if (!aaa) return -1;
1926 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1927 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1935 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1939 if (!cret) return -2;
1940 if (!listing) return -2;
1941 if (*listing) return -2;
1943 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1944 listing, &bytes, cret);
1949 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1951 if (!cret) return -2;
1952 if (!listing) return -2;
1954 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1960 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1964 if (!cret) return -2;
1965 if (session < 0) return -2;
1967 sprintf(aaa, "REQT %d", session);
1968 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1973 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1977 if (!cret) return -2;
1978 if (msgnum < 0) return -2;
1980 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1981 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1986 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1995 /* New SSL object */
1996 temp_ssl = SSL_new(ssl_ctx);
1998 error_printf("SSL_new failed: %s\n",
1999 ERR_reason_error_string(ERR_get_error()));
2002 /* Pointless flag waving */
2003 #if SSLEAY_VERSION_NUMBER >= 0x0922
2004 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
2007 if (!access(EGD_POOL, F_OK))
2010 if (!RAND_status()) {
2011 error_printf("PRNG not properly seeded\n");
2015 /* Associate network connection with SSL object */
2016 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2017 error_printf("SSL_set_fd failed: %s\n",
2018 ERR_reason_error_string(ERR_get_error()));
2022 if (status_hook != NULL)
2023 status_hook("Requesting encryption...\r");
2025 /* Ready to start SSL/TLS */
2027 CtdlIPC_putline(ipc, "STLS");
2028 CtdlIPC_getline(ipc, buf);
2029 if (buf[0] != '2') {
2030 error_printf("Server can't start TLS: %s\n", buf);
2034 r = CtdlIPCGenericCommand(ipc,
2035 "STLS", NULL, 0, NULL, NULL, cret);
2037 error_printf("Server can't start TLS: %s\n", buf);
2042 /* Do SSL/TLS handshake */
2043 if ((a = SSL_connect(temp_ssl)) < 1) {
2044 error_printf("SSL_connect failed: %s\n",
2045 ERR_reason_error_string(ERR_get_error()));
2049 ipc->ssl = temp_ssl;
2051 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2055 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2056 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2057 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2058 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2064 #endif /* HAVE_OPENSSL */
2069 static void endtls(SSL *ssl)
2080 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2085 if (!address) return -2;
2086 if (!cret) return -2;
2088 aaa = (char *)malloc(strlen(address) + 6);
2089 if (!aaa) return -1;
2091 sprintf(aaa, "QDIR %s", address);
2092 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2099 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2103 if (!cret) return -2;
2104 sprintf(aaa, "IPGM %d", secret);
2105 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2110 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2114 if (!cret) return -2;
2115 if (!mret) return -2;
2116 if (*mret) return -2;
2118 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2139 /* ************************************************************************** */
2140 /* Stuff below this line is not for public consumption */
2141 /* ************************************************************************** */
2144 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2146 if (ipc->network_status_cb) ipc->network_status_cb(1);
2147 #ifdef THREADED_CLIENT
2148 pthread_mutex_lock(&(ipc->mutex));
2153 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2155 #ifdef THREADED_CLIENT
2156 pthread_mutex_unlock(&(ipc->mutex));
2158 if (ipc->network_status_cb) ipc->network_status_cb(0);
2162 /* Read a listing from the server up to 000. Append to dest if it exists */
2163 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2172 length = strlen(ret);
2177 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2178 linelength = strlen(aaa);
2179 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2181 strcpy(&ret[length], aaa);
2182 length += linelength;
2183 strcpy(&ret[length++], "\n");
2191 /* Send a listing to the server; generate the ending 000. */
2192 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2196 text = (char *)malloc(strlen(listing) + 6);
2198 strcpy(text, listing);
2199 while (text[strlen(text) - 1] == '\n')
2200 text[strlen(text) - 1] = '\0';
2201 strcat(text, "\n000");
2202 CtdlIPC_putline(ipc, text);
2206 /* Malloc failed but we are committed to send */
2207 /* This may result in extra blanks at the bottom */
2208 CtdlIPC_putline(ipc, text);
2209 CtdlIPC_putline(ipc, "000");
2215 /* Partial read of file from server */
2216 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2218 register size_t len = 0;
2222 if (!cret) return 0;
2223 if (bytes < 1) return 0;
2226 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2227 CtdlIPC_putline(ipc, aaa);
2228 CtdlIPC_getline(ipc, aaa);
2230 strcpy(cret, &aaa[4]);
2232 len = extract_long(&aaa[4], 0);
2233 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2235 /* I know what I'm doing */
2236 serv_read(ipc, ((char *)(*buf) + offset), len);
2238 /* We have to read regardless */
2239 serv_read(ipc, aaa, len);
2243 CtdlIPC_unlock(ipc);
2249 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2253 if (!cret) return -2;
2254 if (!ipc->downloading) return -2;
2256 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2258 ipc->downloading = 0;
2264 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2268 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2269 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2276 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2277 void (*progress_gauge_callback)
2278 (CtdlIPC*, unsigned long, unsigned long),
2281 register size_t len;
2283 if (!cret) return -1;
2284 if (!buf) return -1;
2285 if (*buf) return -1;
2286 if (!ipc->downloading) return -1;
2289 if (progress_gauge_callback)
2290 progress_gauge_callback(ipc, len, bytes);
2291 while (len < bytes) {
2292 register size_t block;
2294 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2300 if (progress_gauge_callback)
2301 progress_gauge_callback(ipc, len, bytes);
2306 /* READ - pipelined */
2307 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2309 void (*progress_gauge_callback)
2310 (CtdlIPC*, unsigned long, unsigned long),
2313 register size_t len;
2314 register int calls; /* How many calls in the pipeline */
2315 register int i; /* iterator */
2318 if (!cret) return -1;
2319 if (!buf) return -1;
2320 if (*buf) return -1;
2321 if (!ipc->downloading) return -1;
2323 *buf = (void *)realloc(*buf, bytes - resume);
2324 if (!*buf) return -1;
2328 if (progress_gauge_callback)
2329 progress_gauge_callback(ipc, len, bytes);
2331 /* How many calls will be in the pipeline? */
2332 calls = (bytes - resume) / 4096;
2333 if ((bytes - resume) % 4096) calls++;
2335 /* Send all requests at once */
2336 for (i = 0; i < calls; i++) {
2337 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2338 CtdlIPC_putline(ipc, aaa);
2341 /* Receive all responses at once */
2342 for (i = 0; i < calls; i++) {
2343 CtdlIPC_getline(ipc, aaa);
2345 strcpy(cret, &aaa[4]);
2347 len = extract_long(&aaa[4], 0);
2348 /* I know what I'm doing */
2349 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2351 if (progress_gauge_callback)
2352 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2354 CtdlIPC_unlock(ipc);
2360 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2365 if (!cret) return -1;
2366 if (!ipc->uploading) return -1;
2368 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2369 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2376 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2377 void (*progress_gauge_callback)
2378 (CtdlIPC*, unsigned long, unsigned long),
2381 register int ret = -1;
2382 register size_t offset = 0;
2386 FILE *fd = uploadFP;
2389 if (!cret) return -1;
2391 fseek(fd, 0L, SEEK_END);
2395 if (progress_gauge_callback)
2396 progress_gauge_callback(ipc, 0, bytes);
2398 while (offset < bytes) {
2399 register size_t to_write;
2401 /* Read some data in */
2402 to_write = fread(buf, 1, 4096, fd);
2404 if (feof(fd) || ferror(fd)) break;
2406 sprintf(aaa, "WRIT %d", (int)to_write);
2407 CtdlIPC_putline(ipc, aaa);
2408 CtdlIPC_getline(ipc, aaa);
2409 strcpy(cret, &aaa[4]);
2411 if (aaa[0] == '7') {
2412 to_write = extract_long(&aaa[4], 0);
2414 serv_write(ipc, buf, to_write);
2416 if (progress_gauge_callback)
2417 progress_gauge_callback(ipc, offset, bytes);
2418 /* Detect short reads and back up if needed */
2419 /* offset will never be negative anyway */
2420 fseek(fd, (signed)offset, SEEK_SET);
2425 if (progress_gauge_callback)
2426 progress_gauge_callback(ipc, 1, 1);
2429 return (!ferr ? ret : -2);
2434 * Generic command method. This method should handle any server command
2435 * except for CHAT. It takes the following arguments:
2437 * ipc The server to speak with
2438 * command Preformatted command to send to server
2439 * to_send A text or binary file to send to server
2440 * (only sent if server requests it)
2441 * bytes_to_send The number of bytes in to_send (required if
2442 * sending binary, optional if sending listing)
2443 * to_receive Pointer to a NULL pointer, if the server
2444 * sends text or binary we will allocate memory
2445 * for the file and stuff it here
2446 * bytes_to_receive If a file is received, we will store its
2448 * proto_response The protocol response. Caller must provide
2449 * this buffer and ensure that it is at least
2450 * 128 bytes in length.
2452 * This function returns a number equal to the protocol response number,
2453 * -1 if an internal error occurred, -2 if caller provided bad values,
2454 * or 0 - the protocol response number if bad values were found during
2455 * the protocol exchange.
2456 * It stores the protocol response string (minus the number) in
2457 * protocol_response as described above. Some commands send additional
2458 * data in this string.
2460 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2461 const char *command, const char *to_send,
2462 size_t bytes_to_send, char **to_receive,
2463 size_t *bytes_to_receive, char *proto_response)
2469 if (!command) return -2;
2470 if (!proto_response) return -2;
2473 if (ipc->ssl) watch_ssl = 1;
2477 CtdlIPC_putline(ipc, command);
2479 CtdlIPC_getline(ipc, proto_response);
2480 if (proto_response[3] == '*')
2482 ret = atoi(proto_response);
2483 strcpy(proto_response, &proto_response[4]);
2484 switch (ret / 100) {
2485 default: /* Unknown, punt */
2487 case 3: /* MORE_DATA */
2489 /* Don't need to do anything */
2491 case 1: /* LISTING_FOLLOWS */
2492 if (to_receive && !*to_receive && bytes_to_receive) {
2493 *to_receive = CtdlIPCReadListing(ipc, NULL);
2494 } else { /* Drain */
2495 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2499 case 4: /* SEND_LISTING */
2501 CtdlIPCSendListing(ipc, to_send);
2503 /* No listing given, fake it */
2504 CtdlIPC_putline(ipc, "000");
2508 case 6: /* BINARY_FOLLOWS */
2509 if (to_receive && !*to_receive && bytes_to_receive) {
2511 extract_long(proto_response, 0);
2512 *to_receive = (char *)
2513 malloc((size_t)*bytes_to_receive);
2517 serv_read(ipc, *to_receive,
2524 drain = extract_long(proto_response, 0);
2525 while (drain > SIZ) {
2526 serv_read(ipc, buf, SIZ);
2529 serv_read(ipc, buf, drain);
2533 case 7: /* SEND_BINARY */
2534 if (to_send && bytes_to_send) {
2535 serv_write(ipc, to_send, bytes_to_send);
2536 } else if (bytes_to_send) {
2537 /* Fake it, send nulls */
2540 fake = bytes_to_send;
2541 memset(buf, '\0', SIZ);
2542 while (fake > SIZ) {
2543 serv_write(ipc, buf, SIZ);
2546 serv_write(ipc, buf, fake);
2548 } /* else who knows? DANGER WILL ROBINSON */
2550 case 8: /* START_CHAT_MODE */
2551 if (!strncasecmp(command, "CHAT", 4)) {
2552 /* Don't call chatmode with generic! */
2553 CtdlIPC_putline(ipc, "/quit");
2556 /* In this mode we send then receive listing */
2558 CtdlIPCSendListing(ipc, to_send);
2560 /* No listing given, fake it */
2561 CtdlIPC_putline(ipc, "000");
2564 if (to_receive && !*to_receive
2565 && bytes_to_receive) {
2566 *to_receive = CtdlIPCReadListing(ipc, NULL);
2567 } else { /* Drain */
2568 while (CtdlIPC_getline(ipc, buf),
2569 strcmp(buf, "000")) ;
2574 case 9: /* ASYNC_MSG */
2575 /* CtdlIPCDoAsync(ret, proto_response); */
2576 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2582 CtdlIPC_unlock(ipc);
2587 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2589 struct hostent *phe;
2590 struct servent *pse;
2591 struct protoent *ppe;
2592 struct sockaddr_in sin;
2595 memset(&sin, 0, sizeof(sin));
2596 sin.sin_family = AF_INET;
2598 pse = getservbyname(service, protocol);
2600 sin.sin_port = pse->s_port;
2602 else if (atoi(service) > 0) {
2603 sin.sin_port = htons(atoi(service));
2606 sin.sin_port = htons(defaultPort);
2608 phe = gethostbyname(host);
2610 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2611 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2614 if ((ppe = getprotobyname(protocol)) == 0) {
2617 if (!strcmp(protocol, "udp")) {
2623 s = socket(PF_INET, type, ppe->p_proto);
2628 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2636 static int uds_connectsock(int *isLocal, char *sockpath)
2638 struct sockaddr_un addr;
2641 memset(&addr, 0, sizeof(addr));
2642 addr.sun_family = AF_UNIX;
2643 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2645 s = socket(AF_UNIX, SOCK_STREAM, 0);
2650 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2661 * input binary data from socket
2663 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2665 unsigned int len, rlen;
2667 #if defined(HAVE_OPENSSL)
2669 serv_read_ssl(ipc, buf, bytes);
2674 while (len < bytes) {
2675 rlen = read(ipc->sock, &buf[len], bytes - len);
2677 connection_died(ipc, 0);
2686 * send binary to server
2688 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2690 unsigned int bytes_written = 0;
2693 #if defined(HAVE_OPENSSL)
2695 serv_write_ssl(ipc, buf, nbytes);
2699 while (bytes_written < nbytes) {
2700 retval = write(ipc->sock, &buf[bytes_written],
2701 nbytes - bytes_written);
2703 connection_died(ipc, 0);
2706 bytes_written += retval;
2713 * input binary data from encrypted connection
2715 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2721 while (len < bytes) {
2722 if (SSL_want_read(ipc->ssl)) {
2723 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2724 error_printf("SSL_write in serv_read:\n");
2725 ERR_print_errors_fp(stderr);
2728 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2732 errval = SSL_get_error(ipc->ssl, rlen);
2733 if (errval == SSL_ERROR_WANT_READ ||
2734 errval == SSL_ERROR_WANT_WRITE) {
2739 Not sure why we'd want to handle these error codes any differently,
2740 but this definitely isn't the way to handle them. Someone must have
2741 naively assumed that we could fall back to unencrypted communications,
2742 but all it does is just recursively blow the stack.
2743 if (errval == SSL_ERROR_ZERO_RETURN ||
2744 errval == SSL_ERROR_SSL) {
2745 serv_read(ipc, &buf[len], bytes - len);
2749 error_printf("SSL_read in serv_read: %s\n",
2750 ERR_reason_error_string(ERR_peek_error()));
2751 connection_died(ipc, 1);
2760 * send binary to server encrypted
2762 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2764 unsigned int bytes_written = 0;
2768 while (bytes_written < nbytes) {
2769 if (SSL_want_write(ipc->ssl)) {
2770 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2771 error_printf("SSL_read in serv_write:\n");
2772 ERR_print_errors_fp(stderr);
2775 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2776 nbytes - bytes_written);
2780 errval = SSL_get_error(ipc->ssl, retval);
2781 if (errval == SSL_ERROR_WANT_READ ||
2782 errval == SSL_ERROR_WANT_WRITE) {
2786 if (errval == SSL_ERROR_ZERO_RETURN ||
2787 errval == SSL_ERROR_SSL) {
2788 serv_write(ipc, &buf[bytes_written],
2789 nbytes - bytes_written);
2792 error_printf("SSL_write in serv_write: %s\n",
2793 ERR_reason_error_string(ERR_peek_error()));
2794 connection_died(ipc, 1);
2797 bytes_written += retval;
2802 #ifdef THREADED_CLIENT
2803 static void ssl_lock(int mode, int n, const char *file, int line)
2805 if (mode & CRYPTO_LOCK)
2806 pthread_mutex_lock(Critters[n]);
2808 pthread_mutex_unlock(Critters[n]);
2810 #endif /* THREADED_CLIENT */
2813 static void CtdlIPC_init_OpenSSL(void)
2816 SSL_METHOD *ssl_method;
2819 /* already done init */
2828 SSL_load_error_strings();
2829 SSLeay_add_ssl_algorithms();
2831 /* Set up the SSL context in which we will oeprate */
2832 ssl_method = SSLv23_client_method();
2833 ssl_ctx = SSL_CTX_new(ssl_method);
2835 error_printf("SSL_CTX_new failed: %s\n",
2836 ERR_reason_error_string(ERR_get_error()));
2839 /* Any reasonable cipher we can get */
2840 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2841 error_printf("No ciphers available for encryption\n");
2844 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2846 /* Load DH parameters into the context */
2849 error_printf("Can't allocate a DH object: %s\n",
2850 ERR_reason_error_string(ERR_get_error()));
2853 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2854 error_printf("Can't assign DH_P: %s\n",
2855 ERR_reason_error_string(ERR_get_error()));
2859 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2860 error_printf("Can't assign DH_G: %s\n",
2861 ERR_reason_error_string(ERR_get_error()));
2866 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2869 #ifdef THREADED_CLIENT
2870 /* OpenSSL requires callbacks for threaded clients */
2871 CRYPTO_set_locking_callback(ssl_lock);
2872 CRYPTO_set_id_callback(id_callback);
2874 /* OpenSSL requires us to do semaphores for threaded clients */
2875 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2877 perror("malloc failed");
2880 for (a = 0; a < CRYPTO_num_locks(); a++) {
2881 Critters[a] = malloc(sizeof (pthread_mutex_t));
2883 perror("malloc failed");
2886 pthread_mutex_init(Critters[a], NULL);
2889 #endif /* THREADED_CLIENT */
2894 #ifdef THREADED_CLIENT
2895 static unsigned long id_callback(void) {
2896 return (unsigned long)pthread_self();
2898 #endif /* THREADED_CLIENT */
2899 #endif /* HAVE_OPENSSL */
2903 ReadNetworkChunk(CtdlIPC* ipc)
2920 FD_SET(ipc->sock, &read_fd);
2921 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2923 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2927 *(ipc->BufPtr) = '\0';
2928 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2929 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2931 ipc->BufPtr[n]='\0';
2939 if (!(errno == EINTR || errno == EAGAIN))
2940 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2946 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2948 ipc->BufPtr[n]='\0';
2953 connection_died(ipc, 0);
2961 * input string from socket - implemented in terms of serv_read()
2965 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2968 char *aptr, *bptr, *aeptr, *beptr;
2970 // error_printf("---\n");
2973 #if defined(HAVE_OPENSSL)
2976 /* Read one character at a time. */
2978 serv_read(ipc, &buf[i], 1);
2979 if (buf[i] == '\n' || i == (SIZ-1))
2983 /* If we got a long line, discard characters until the newline. */
2985 while (buf[i] != '\n')
2986 serv_read(ipc, &buf[i], 1);
2988 /* Strip the trailing newline (and carriage return, if present) */
2989 if (i>=0 && buf[i] == 10) buf[i--] = 0;
2990 if (i>=0 && buf[i] == 13) buf[i--] = 0;
2995 if (ipc->Buf == NULL)
2998 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3000 ipc->BufPtr = ipc->Buf;
3004 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3005 if (ipc->BufUsed == 0)
3006 ReadNetworkChunk(ipc);
3008 //// if (ipc->BufUsed != 0) while (1)
3014 aeptr = ipc->Buf + ipc->BufSize;
3015 while ((aptr < aeptr) &&
3019 *(bptr++) = *(aptr++);
3020 if ((*aptr == '\n') && (aptr < aeptr))
3022 /* Terminate it right, remove the line breaks */
3023 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3025 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3028 // 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);
3029 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3032 /* is there more in the buffer we need to read later? */
3033 if (ipc->Buf + ipc->BufUsed > aptr)
3040 ipc->BufPtr = ipc->Buf;
3042 // error_printf("----bla6\n");
3045 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3046 else if ((ipc->BufPtr != ipc->Buf) &&
3047 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3049 size_t NewBufSize = ipc->BufSize * 2;
3050 int delta = (ipc->BufPtr - ipc->Buf);
3053 /* if the line would end after our buffer, we should use a bigger buffer. */
3054 NewBuf = (char *)malloc (NewBufSize + 10);
3055 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3057 ipc->Buf = ipc->BufPtr = NewBuf;
3058 ipc->BufUsed -= delta;
3059 ipc->BufSize = NewBufSize;
3061 if (ReadNetworkChunk(ipc) <0)
3063 // error_printf("----bla\n");
3067 /// error_printf("----bl45761%s\nipc->BufUsed");
3069 // error_printf("----bla1\n");
3072 #else /* CHUNKED_READ */
3074 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3078 /* Read one character at a time. */
3080 serv_read(ipc, &buf[i], 1);
3081 if (buf[i] == '\n' || i == (SIZ-1))
3085 /* If we got a long line, discard characters until the newline. */
3087 while (buf[i] != '\n')
3088 serv_read(ipc, &buf[i], 1);
3090 /* Strip the trailing newline (and carriage return, if present) */
3091 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3092 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3096 #endif /* CHUNKED_READ */
3099 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3101 CtdlIPC_getline(ipc, buf);
3105 * send line to server - implemented in terms of serv_write()
3107 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3113 cmd = malloc(len + 2);
3115 /* This requires no extra memory */
3116 serv_write(ipc, buf, len);
3117 serv_write(ipc, "\n", 1);
3119 /* This is network-optimized */
3120 strncpy(cmd, buf, len);
3121 strcpy(cmd + len, "\n");
3122 serv_write(ipc, cmd, len + 1);
3126 ipc->last_command_sent = time(NULL);
3129 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3131 CtdlIPC_putline(ipc, buf);
3138 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3146 ipc = ialloc(CtdlIPC);
3150 #if defined(HAVE_OPENSSL)
3152 CtdlIPC_init_OpenSSL();
3154 #if defined(HAVE_PTHREAD_H)
3155 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3157 ipc->sock = -1; /* Not connected */
3158 ipc->isLocal = 0; /* Not local, of course! */
3159 ipc->downloading = 0;
3161 ipc->last_command_sent = 0L;
3162 ipc->network_status_cb = NULL;
3167 strcpy(cithost, DEFAULT_HOST); /* default host */
3168 strcpy(citport, DEFAULT_PORT); /* default port */
3170 /* Allow caller to supply our values (Windows) */
3171 if (hostbuf && strlen(hostbuf) > 0)
3172 strcpy(cithost, hostbuf);
3173 if (portbuf && strlen(portbuf) > 0)
3174 strcpy(citport, portbuf);
3176 /* Read host/port from command line if present */
3177 for (a = 0; a < argc; ++a) {
3180 } else if (a == 1) {
3181 strcpy(cithost, argv[a]);
3182 } else if (a == 2) {
3183 strcpy(citport, argv[a]);
3185 error_printf("%s: usage: ",argv[0]);
3186 error_printf("%s [host] [port] ",argv[0]);
3193 if ((!strcmp(cithost, "localhost"))
3194 || (!strcmp(cithost, "127.0.0.1"))) {
3198 /* If we're using a unix domain socket we can do a bunch of stuff */
3199 if (!strcmp(cithost, UDS)) {
3200 if (!strcasecmp(citport, DEFAULT_PORT)) {
3201 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3204 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3206 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3207 if (ipc->sock == -1) {
3211 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3212 if (portbuf != NULL) strcpy(portbuf, sockpath);
3216 ipc->sock = connectsock(cithost, citport, "tcp", 504);
3217 if (ipc->sock == -1) {
3221 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3222 if (portbuf != NULL) strcpy(portbuf, citport);
3228 * Disconnect and delete the IPC class (destructor)
3230 void CtdlIPC_delete(CtdlIPC* ipc)
3234 SSL_shutdown(ipc->ssl);
3239 if (ipc->sock > -1) {
3240 shutdown(ipc->sock, 2); /* Close it up */
3243 if (ipc->Buf != NULL)
3252 * Disconnect and delete the IPC class (destructor)
3253 * Also NULLs out the pointer
3255 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3257 CtdlIPC_delete(*pipc);
3263 * return the file descriptor of the server socket so we can select() on it.
3265 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3268 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3275 * return one character
3277 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3280 char CtdlIPC_get(CtdlIPC* ipc)
3285 serv_read(ipc, buf, 1);