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 const char *svn_revision(void);
87 * Does nothing. The server should always return 200.
89 int CtdlIPCNoop(CtdlIPC *ipc)
93 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
98 * Does nothing interesting. The server should always return 200
99 * along with your string.
101 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
107 if (!cret) return -2;
109 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
112 sprintf(aaa, "ECHO %s", arg);
113 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
120 * Asks the server to close the connecction.
121 * Should always return 200.
123 int CtdlIPCQuit(CtdlIPC *ipc)
125 register int ret = 221; /* Default to successful quit */
129 if (ipc->sock > -1) {
130 CtdlIPC_putline(ipc, "QUIT");
131 CtdlIPC_getline(ipc, aaa);
136 SSL_shutdown(ipc->ssl);
140 shutdown(ipc->sock, 2); /* Close connection; we're dead */
148 * Asks the server to log out. Should always return 200, even if no user
149 * was logged in. The user will not be logged in after this!
151 int CtdlIPCLogout(CtdlIPC *ipc)
157 CtdlIPC_putline(ipc, "LOUT");
158 CtdlIPC_getline(ipc, aaa);
166 * First stage of authentication - pass the username. Returns 300 if the
167 * username is able to log in, with the username correctly spelled in cret.
168 * Returns various 500 error codes if the user doesn't exist, etc.
170 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
175 if (!username) return -2;
176 if (!cret) return -2;
178 aaa = (char *)malloc((size_t)(strlen(username) + 6));
181 sprintf(aaa, "USER %s", username);
182 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
189 * Second stage of authentication - provide password. The server returns
190 * 200 and several arguments in cret relating to the user's account.
192 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
197 if (!passwd) return -2;
198 if (!cret) return -2;
200 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
203 sprintf(aaa, "PASS %s", passwd);
204 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
211 * Second stage of authentication - provide password. The server returns
212 * 200 and several arguments in cret relating to the user's account.
214 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
219 if (!response) return -2;
220 if (!cret) return -2;
222 aaa = (char *)malloc((size_t)(strlen(response) + 6));
225 sprintf(aaa, "PAS2 %s", response);
226 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
233 * Create a new user. This returns 200 plus the same arguments as TryPassword
234 * if selfservice is nonzero, unless there was a problem creating the account.
235 * If selfservice is zero, creates a new user but does not log out the existing
236 * user - intended for use by system administrators to create accounts on
237 * behalf of other users.
239 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
244 if (!username) return -2;
245 if (!cret) return -2;
247 aaa = (char *)malloc((size_t)(strlen(username) + 6));
250 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
251 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
258 * Changes the user's password. Returns 200 if changed, errors otherwise.
260 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
265 if (!passwd) return -2;
266 if (!cret) return -2;
268 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
271 sprintf(aaa, "SETP %s", passwd);
272 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
279 /* Caller must free the march list */
280 /* Room types are defined in enum RoomList; keep these in sync! */
281 /* floor is -1 for all, or floornum */
282 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
285 struct march *march = NULL;
286 static char *proto[] =
287 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
292 if (!listing) return -2;
293 if (*listing) return -2; /* Free the listing first */
294 if (!cret) return -2;
295 /* if (which < 0 || which > 4) return -2; */
296 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
298 sprintf(aaa, "%s %d", proto[which], floor);
299 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
300 if (ret / 100 == 1) {
303 while (bbb && strlen(bbb)) {
306 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
308 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
309 mptr = (struct march *) malloc(sizeof (struct march));
312 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
313 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
314 mptr->march_floor = (char) extract_int(aaa, 2);
315 mptr->march_order = (char) extract_int(aaa, 3);
316 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
317 mptr->march_access = (char) extract_int(aaa, 5);
324 while (mptr2->next != NULL)
338 /* Caller must free the struct ctdluser; caller may pass an existing one */
339 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
343 if (!cret) return -2;
344 if (!uret) return -2;
345 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
346 if (!*uret) return -1;
348 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
349 if (ret / 100 == 2) {
350 uret[0]->USscreenwidth = extract_int(cret, 0);
351 uret[0]->USscreenheight = extract_int(cret, 1);
352 uret[0]->flags = extract_int(cret, 2);
359 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
363 if (!uret) return -2;
364 if (!cret) return -2;
366 sprintf(aaa, "SETU %d|%d|%d",
367 uret->USscreenwidth, uret->USscreenheight,
369 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
374 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
379 if (!oldname) return -2;
380 if (!newname) return -2;
381 if (!cret) return -2;
383 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
384 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
390 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
391 struct ctdlipcroom **rret, char *cret)
396 if (!cret) return -2;
397 if (!rret) return -2;
398 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
399 if (!*rret) return -1;
402 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
407 sprintf(aaa, "GOTO %s|%s", room, passwd);
409 aaa = (char *)malloc(strlen(room) + 6);
414 sprintf(aaa, "GOTO %s", room);
416 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
417 if (ret / 100 == 2) {
418 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
419 rret[0]->RRunread = extract_long(cret, 1);
420 rret[0]->RRtotal = extract_long(cret, 2);
421 rret[0]->RRinfoupdated = extract_int(cret, 3);
422 rret[0]->RRflags = extract_int(cret, 4);
423 rret[0]->RRhighest = extract_long(cret, 5);
424 rret[0]->RRlastread = extract_long(cret, 6);
425 rret[0]->RRismailbox = extract_int(cret, 7);
426 rret[0]->RRaide = extract_int(cret, 8);
427 rret[0]->RRnewmail = extract_long(cret, 9);
428 rret[0]->RRfloor = extract_int(cret, 10);
429 rret[0]->RRflags2 = extract_int(cret, 14);
440 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
441 /* whicharg is number of messages, applies to last, first, gt, lt */
442 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
443 const char *mtemplate, unsigned long **mret, char *cret)
446 register unsigned long count = 0;
447 static char *proto[] =
448 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
453 if (!cret) return -2;
454 if (!mret) return -2;
455 if (*mret) return -2;
456 if (which < 0 || which > 6) return -2;
459 sprintf(aaa, "MSGS %s||%d", proto[which],
460 (mtemplate) ? 1 : 0);
462 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
463 (mtemplate) ? 1 : 0);
464 if (mtemplate) count = strlen(mtemplate);
465 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
469 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
472 while (bbb && strlen(bbb)) {
473 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
474 remove_token(bbb, 0, '\n');
475 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
476 sizeof (unsigned long)));
478 (*mret)[count++] = atol(aaa);
490 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
491 struct ctdlipcmessage **mret, char *cret)
497 int multipart_hunting = 0;
498 char multipart_prefix[128];
501 if (!cret) return -1;
502 if (!mret) return -1;
503 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
504 if (!*mret) return -1;
505 if (!msgnum) return -1;
507 strcpy(encoding, "");
508 strcpy(mret[0]->content_type, "");
509 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
510 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
511 if (ret / 100 == 1) {
513 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
514 while (strlen(bbb) > 4 && bbb[4] == '=') {
515 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
516 remove_token(bbb, 0, '\n');
518 if (!strncasecmp(aaa, "nhdr=yes", 8))
520 else if (!strncasecmp(aaa, "from=", 5))
521 safestrncpy(mret[0]->author, &aaa[5], SIZ);
522 else if (!strncasecmp(aaa, "type=", 5))
523 mret[0]->type = atoi(&aaa[5]);
524 else if (!strncasecmp(aaa, "msgn=", 5))
525 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
526 else if (!strncasecmp(aaa, "subj=", 5))
527 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
528 else if (!strncasecmp(aaa, "rfca=", 5))
529 safestrncpy(mret[0]->email, &aaa[5], SIZ);
530 else if (!strncasecmp(aaa, "hnod=", 5))
531 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
532 else if (!strncasecmp(aaa, "room=", 5))
533 safestrncpy(mret[0]->room, &aaa[5], SIZ);
534 else if (!strncasecmp(aaa, "node=", 5))
535 safestrncpy(mret[0]->node, &aaa[5], SIZ);
536 else if (!strncasecmp(aaa, "rcpt=", 5))
537 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
538 else if (!strncasecmp(aaa, "wefw=", 5))
539 safestrncpy(mret[0]->references, &aaa[5], SIZ);
540 else if (!strncasecmp(aaa, "time=", 5))
541 mret[0]->time = atol(&aaa[5]);
543 /* Multipart/alternative prefix & suffix strings help
544 * us to determine which part we want to download.
546 else if (!strncasecmp(aaa, "pref=", 5)) {
547 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
548 if (!strcasecmp(multipart_prefix,
549 "multipart/alternative")) {
553 else if (!strncasecmp(aaa, "suff=", 5)) {
554 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
555 if (!strcasecmp(multipart_prefix,
556 "multipart/alternative")) {
561 else if (!strncasecmp(aaa, "part=", 5)) {
562 struct parts *ptr, *chain;
564 ptr = (struct parts *)calloc(1, sizeof (struct parts));
567 /* Fill the buffers for the caller */
568 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
569 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
570 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
571 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
572 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
573 ptr->length = extract_long(&aaa[5], 5);
574 if (!mret[0]->attachments)
575 mret[0]->attachments = ptr;
577 chain = mret[0]->attachments;
583 /* Now handle multipart/alternative */
584 if (multipart_hunting > 0) {
585 if ( (!strcasecmp(ptr->mimetype,
587 || (!strcasecmp(ptr->mimetype,
589 strcpy(mret[0]->mime_chosen,
597 /* Eliminate "text\n" */
598 remove_token(bbb, 0, '\n');
600 /* If doing a MIME thing, pull out the extra headers */
603 if (!strncasecmp(bbb, "Content-type:", 13)) {
604 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
605 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
606 striplt(mret[0]->content_type);
608 /* strip out ";charset=" portion. FIXME do something with
609 * the charset (like... convert it) instead of just throwing
612 if (strstr(mret[0]->content_type, ";") != NULL) {
613 strcpy(strstr(mret[0]->content_type, ";"), "");
617 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
618 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
619 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
620 striplt(mret[0]->mime_chosen);
622 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
623 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
624 strcpy(encoding, &encoding[26]);
627 remove_token(bbb, 0, '\n');
628 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
629 remove_token(bbb, 0, '\n');
636 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
638 int bytes_decoded = 0;
639 ccc = malloc(strlen(bbb) + 32768);
640 if (!strcasecmp(encoding, "base64")) {
641 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
643 else if (!strcasecmp(encoding, "quoted-printable")) {
644 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
646 ccc[bytes_decoded] = 0;
651 /* FIXME: Strip trailing whitespace */
652 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
655 bbb = (char *)realloc(bbb, 1);
665 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
670 if (!cret) return -2;
671 if (!listing) return -2;
672 if (*listing) return -2;
674 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
680 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
684 char *listing = NULL;
687 if (!cret) return -2;
689 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
690 if (ret / 100 == 1) {
693 while (*listing && strlen(listing)) {
694 extract_token(buf, listing, 0, '\n', sizeof buf);
695 remove_token(listing, 0, '\n');
697 case 0: ipc->ServInfo.pid = atoi(buf);
699 case 1: strcpy(ipc->ServInfo.nodename,buf);
701 case 2: strcpy(ipc->ServInfo.humannode,buf);
703 case 3: strcpy(ipc->ServInfo.fqdn,buf);
705 case 4: strcpy(ipc->ServInfo.software,buf);
707 case 5: ipc->ServInfo.rev_level = atoi(buf);
709 case 6: strcpy(ipc->ServInfo.site_location,buf);
711 case 7: strcpy(ipc->ServInfo.sysadm,buf);
713 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
715 case 10: ipc->ServInfo.ok_floors = atoi(buf);
717 case 11: ipc->ServInfo.paging_level = atoi(buf);
719 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
721 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
723 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
725 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
727 case 17: ipc->ServInfo.load_avg = atof(buf);
729 case 18: ipc->ServInfo.worker_avg = atof(buf);
731 case 19: ipc->ServInfo.thread_count = atoi(buf);
733 case 20: ipc->ServInfo.has_sieve = atoi(buf);
735 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
737 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
743 if (listing) free(listing);
749 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
754 if (!cret) return -2;
755 if (!listing) return -2;
756 if (*listing) return -2;
758 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
764 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
766 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
771 if (!cret) return -2;
774 sprintf(aaa, "SLRP %ld", msgnum);
777 sprintf(aaa, "SLRP HIGHEST");
779 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
785 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
790 if (!cret) return -2;
791 if (!username) return -2;
793 aaa = (char *)malloc(strlen(username) + 6);
796 sprintf(aaa, "INVT %s", username);
797 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
804 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
809 if (!cret) return -1;
810 if (!username) return -1;
812 aaa = (char *)malloc(strlen(username) + 6);
814 sprintf(aaa, "KICK %s", username);
815 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
822 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
826 if (!cret) return -2;
827 if (!qret) return -2;
828 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
829 if (!*qret) return -1;
831 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
832 if (ret / 100 == 2) {
833 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
834 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
835 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
836 qret[0]->QRflags = extract_int(cret, 3);
837 qret[0]->QRfloor = extract_int(cret, 4);
838 qret[0]->QRorder = extract_int(cret, 5);
839 qret[0]->QRdefaultview = extract_int(cret, 6);
840 qret[0]->QRflags2 = extract_int(cret, 7);
847 /* set forget to kick all users out of room */
848 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
853 if (!cret) return -2;
854 if (!qret) return -2;
856 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
857 strlen(qret->QRdirname) + 64);
860 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
861 qret->QRname, qret->QRpasswd, qret->QRdirname,
862 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
863 qret->QRdefaultview, qret->QRflags2);
864 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
871 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
873 if (!cret) return -1;
875 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
880 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
885 if (!cret) return -2;
886 if (!username) return -2;
888 aaa = (char *)malloc(strlen(username) + 6);
891 sprintf(aaa, "SETA %s", username);
892 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
899 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
905 if (!cret) return -2;
908 if (mr->references) {
909 for (ptr=mr->references; *ptr != 0; ++ptr) {
910 if (*ptr == '|') *ptr = '!';
914 snprintf(cmd, sizeof cmd,
915 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
916 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
917 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
919 if ((flag == 0) && (subject_required != NULL)) {
920 /* Is the server strongly recommending that the user enter a message subject? */
921 if ((cret[3] != '\0') && (cret[4] != '\0')) {
922 *subject_required = extract_int(&cret[4], 1);
932 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
936 if (!cret) return -2;
937 if (!iret) return -2;
938 if (*iret) return -2;
940 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
945 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
949 if (!cret) return -2;
950 if (!msgnum) return -2;
952 sprintf(aaa, "DELE %ld", msgnum);
953 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
958 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
963 if (!cret) return -2;
964 if (!destroom) return -2;
965 if (!msgnum) return -2;
967 aaa = (char *)malloc(strlen(destroom) + 28);
970 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
971 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
978 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
982 if (!cret) return -2;
984 sprintf(aaa, "KILL %d", for_real);
985 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
990 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
991 const char *password, int floor, char *cret)
996 if (!cret) return -2;
997 if (!roomname) return -2;
1000 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
1001 if (!aaa) return -1;
1002 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
1005 aaa = (char *)malloc(strlen(roomname) + 40);
1006 if (!aaa) return -1;
1007 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1010 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1017 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1019 if (!cret) return -2;
1021 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1026 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1032 if (!cret) return -2;
1033 if (!mret) return -2;
1034 if (*mret) return -2;
1035 if (!message) return -2;
1037 aaa = (char *)malloc(strlen(message) + 6);
1038 if (!aaa) return -1;
1040 sprintf(aaa, "MESG %s", message);
1041 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1048 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1050 if (!cret) return -2;
1052 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1057 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1063 if (!cret) return -2;
1064 if (!rret) return -2;
1065 if (*rret) return -2;
1068 aaa = (char *)malloc(strlen(username) + 6);
1070 aaa = (char *)malloc(12);
1071 if (!aaa) return -1;
1074 sprintf(aaa, "GREG %s", username);
1076 sprintf(aaa, "GREG _SELF_");
1077 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1084 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1089 if (!cret) return -2;
1090 if (!username) return -2;
1091 if (axlevel < 0 || axlevel > 7) return -2;
1093 aaa = (char *)malloc(strlen(username) + 17);
1094 if (!aaa) return -1;
1096 sprintf(aaa, "VALI %s|%d", username, axlevel);
1097 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1104 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1108 if (!cret) return -1;
1109 if (!info) return -1;
1111 sprintf(aaa, "EINF %d", for_real);
1112 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1117 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1123 if (!cret) return -1;
1124 if (!listing) return -1;
1125 if (*listing) return -1;
1126 if (!searchstring) return -1;
1128 cmd = malloc(strlen(searchstring) + 10);
1129 sprintf(cmd, "LIST %s", searchstring);
1131 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1138 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1140 if (!cret) return -1;
1141 if (!info) return -1;
1143 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1149 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1153 if (!cret) return -1;
1154 if (!chek) return -1;
1156 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1157 if (ret / 100 == 2) {
1158 chek->newmail = extract_long(cret, 0);
1159 chek->needregis = extract_int(cret, 1);
1160 chek->needvalid = extract_int(cret, 2);
1167 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1172 if (!cret) return -2;
1173 if (!filename) return -2;
1175 aaa = (char *)malloc(strlen(filename) + 6);
1176 if (!aaa) return -1;
1178 sprintf(aaa, "DELF %s", filename);
1179 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1186 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1191 if (!cret) return -2;
1192 if (!filename) return -2;
1193 if (!destroom) return -2;
1195 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1196 if (!aaa) return -1;
1198 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1199 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1206 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1211 if (!cret) return -2;
1212 if (!filename) return -2;
1213 if (!destnode) return -2;
1215 aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1216 if (!aaa) return -1;
1218 sprintf(aaa, "NETF %s|%s", filename, destnode);
1219 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1226 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1231 if (!cret) return -1;
1232 if (!listing) return -1;
1233 if (*listing) return -1;
1235 *stamp = CtdlIPCServerTime(ipc, cret);
1237 *stamp = time(NULL);
1238 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1244 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1246 void (*progress_gauge_callback)
1247 (CtdlIPC*, unsigned long, unsigned long),
1256 if (!cret) return -2;
1257 if (!filename) return -2;
1258 if (!buf) return -2;
1259 if (*buf) return -2;
1260 if (ipc->downloading) return -2;
1262 aaa = (char *)malloc(strlen(filename) + 6);
1263 if (!aaa) return -1;
1265 sprintf(aaa, "OPEN %s", filename);
1266 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1268 if (ret / 100 == 2) {
1269 ipc->downloading = 1;
1270 bytes = extract_long(cret, 0);
1271 last_mod = extract_int(cret, 1);
1272 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1274 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1275 progress_gauge_callback, cret);
1277 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1278 progress_gauge_callback, cret);
1281 ret = CtdlIPCEndDownload(ipc, cret);
1283 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1284 filename, mimetype);
1291 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1293 void (*progress_gauge_callback)
1294 (CtdlIPC*, unsigned long, unsigned long),
1304 if (!cret) return -2;
1305 if (!buf) return -2;
1306 if (*buf) return -2;
1307 if (!part) return -2;
1308 if (!msgnum) return -2;
1309 if (ipc->downloading) return -2;
1311 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1312 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1313 if (ret / 100 == 2) {
1314 ipc->downloading = 1;
1315 bytes = extract_long(cret, 0);
1316 last_mod = extract_int(cret, 1);
1317 extract_token(filename, cret, 2, '|', sizeof filename);
1318 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1319 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1320 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1321 ret = CtdlIPCEndDownload(ipc, cret);
1323 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1324 filename, mimetype);
1331 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1332 void (*progress_gauge_callback)
1333 (CtdlIPC*, unsigned long, unsigned long),
1342 if (!cret) return -1;
1343 if (!buf) return -1;
1344 if (*buf) return -1;
1345 if (!filename) return -1;
1346 if (ipc->downloading) return -1;
1348 aaa = (char *)malloc(strlen(filename) + 6);
1349 if (!aaa) return -1;
1351 sprintf(aaa, "OIMG %s", filename);
1352 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1354 if (ret / 100 == 2) {
1355 ipc->downloading = 1;
1356 bytes = extract_long(cret, 0);
1357 last_mod = extract_int(cret, 1);
1358 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1359 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1360 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1361 ret = CtdlIPCEndDownload(ipc, cret);
1363 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1364 filename, mimetype);
1371 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1373 void (*progress_gauge_callback)
1374 (CtdlIPC*, unsigned long, unsigned long),
1380 char MimeTestBuf[64];
1381 const char *MimeType;
1384 if (!cret) return -1;
1385 if (!save_as) return -1;
1386 if (!comment) return -1;
1387 if (!path) return -1;
1388 if (!*path) return -1;
1389 if (ipc->uploading) return -1;
1391 uploadFP = fopen(path, "r");
1392 if (!uploadFP) return -2;
1394 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1399 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1400 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1401 if (!aaa) return -1;
1403 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1404 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1406 if (ret / 100 == 2) {
1408 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1409 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1417 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1418 const char *save_as,
1419 void (*progress_gauge_callback)
1420 (CtdlIPC*, unsigned long, unsigned long),
1426 char MimeTestBuf[64];
1427 const char *MimeType;
1430 if (!cret) return -1;
1431 if (!save_as) return -1;
1432 if (!path && for_real) return -1;
1433 if (!*path && for_real) return -1;
1434 if (ipc->uploading) return -1;
1436 aaa = (char *)malloc(strlen(save_as) + 17);
1437 if (!aaa) return -1;
1439 uploadFP = fopen(path, "r");
1440 if (!uploadFP) return -2;
1442 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1446 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1448 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1449 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1451 if (ret / 100 == 2 && for_real) {
1453 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1454 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1462 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1467 if (!cret) return -2;
1468 if (!username) return -2;
1470 aaa = (char *)malloc(strlen(username) + 6);
1471 if (!aaa) return -1;
1473 sprintf(aaa, "QUSR %s", username);
1474 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1481 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1485 if (!cret) return -2;
1486 if (!listing) return -2;
1487 if (*listing) return -2;
1489 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1494 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1499 if (!cret) return -2;
1500 if (!name) return -2;
1502 sprintf(aaa, "CFLR %s|%d", name, for_real);
1503 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1509 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1513 if (!cret) return -1;
1514 if (floornum < 0) return -1;
1516 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1517 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1522 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1527 if (!cret) return -2;
1528 if (!floorname) return -2;
1529 if (floornum < 0) return -2;
1531 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1532 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1540 * You only need to fill out hostname, the defaults will be used if any of the
1541 * other fields are not set properly.
1543 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1544 int revision, const char *software_name, const char *hostname,
1550 if (developerid < 0 || clientid < 0 || revision < 0 ||
1554 revision = REV_LEVEL - 600;
1555 software_name = "Citadel (libcitadel)";
1557 if (!hostname) return -2;
1559 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1560 if (!aaa) return -1;
1562 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1563 revision, software_name, hostname);
1564 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1571 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1577 if (!cret) return -2;
1578 if (!username) return -2;
1580 aaa = (char *)malloc(strlen(username) + 8);
1581 if (!aaa) return -1;
1584 sprintf(aaa, "SEXP %s|-", username);
1585 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1588 sprintf(aaa, "SEXP %s||", username);
1589 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1597 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1601 if (!cret) return -2;
1602 if (!listing) return -2;
1603 if (*listing) return -2;
1605 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1610 /* mode is 0 = enable, 1 = disable, 2 = status */
1611 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1615 if (!cret) return -2;
1617 sprintf(aaa, "DEXP %d", mode);
1618 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1623 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1625 if (!cret) return -2;
1626 if (!bio) return -2;
1628 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1634 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1640 if (!cret) return -2;
1641 if (!username) return -2;
1642 if (!listing) return -2;
1643 if (*listing) return -2;
1645 aaa = (char *)malloc(strlen(username) + 6);
1646 if (!aaa) return -1;
1648 sprintf(aaa, "RBIO %s", username);
1649 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1656 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1660 if (!cret) return -2;
1661 if (!listing) return -2;
1662 if (*listing) return -2;
1664 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1669 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1673 if (!cret) return -1;
1675 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1676 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1681 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1685 if (!cret) return -1;
1687 sprintf(aaa, "TERM %d", sid);
1688 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1693 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1695 if (!cret) return -1;
1697 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1702 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1706 if (!cret) return -1;
1708 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1709 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1714 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1720 if (!cret) return -2;
1721 if (!text) return -2;
1722 if (!filename) return -2;
1724 aaa = (char *)malloc(strlen(filename) + 6);
1725 if (!aaa) return -1;
1727 sprintf(aaa, "EMSG %s", filename);
1728 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1735 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1740 if (!cret) return -2;
1741 if (!hostname) return -2;
1743 aaa = (char *)malloc(strlen(hostname) + 6);
1744 if (!aaa) return -1;
1746 sprintf(aaa, "HCHG %s", hostname);
1747 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1754 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1759 if (!cret) return -2;
1760 if (!roomname) return -2;
1762 aaa = (char *)malloc(strlen(roomname) + 6);
1763 if (!aaa) return -1;
1765 sprintf(aaa, "RCHG %s", roomname);
1766 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1773 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1778 if (!cret) return -2;
1779 if (!username) return -2;
1781 aaa = (char *)malloc(strlen(username) + 6);
1782 if (!aaa) return -1;
1784 sprintf(aaa, "UCHG %s", username);
1785 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1792 /* This function returns the actual server time reported, or 0 if error */
1793 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1795 register time_t tret;
1798 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1799 if (ret / 100 == 2) {
1800 tret = extract_long(cret, 0);
1809 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1810 struct ctdluser **uret, char *cret)
1815 if (!cret) return -2;
1816 if (!uret) return -2;
1817 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1818 if (!*uret) return -1;
1820 sprintf(aaa, "AGUP %s", who);
1821 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1823 if (ret / 100 == 2) {
1824 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1825 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1826 uret[0]->flags = extract_int(cret, 2);
1827 uret[0]->timescalled = extract_long(cret, 3);
1828 uret[0]->posted = extract_long(cret, 4);
1829 uret[0]->axlevel = extract_int(cret, 5);
1830 uret[0]->usernum = extract_long(cret, 6);
1831 uret[0]->lastcall = extract_long(cret, 7);
1832 uret[0]->USuserpurge = extract_int(cret, 8);
1839 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1844 if (!cret) return -2;
1845 if (!uret) return -2;
1847 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1848 if (!aaa) return -1;
1850 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1851 uret->fullname, uret->password, uret->flags,
1852 uret->timescalled, uret->posted, uret->axlevel,
1853 uret->usernum, uret->lastcall, uret->USuserpurge);
1854 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1861 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1862 /* caller must free the struct ExpirePolicy */
1863 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1864 struct ExpirePolicy **policy, char *cret)
1866 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1870 if (!cret) return -2;
1871 if (!policy) return -2;
1872 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1873 if (!*policy) return -1;
1874 if (which < 0 || which > 3) return -2;
1876 sprintf(cmd, "GPEX %s", proto[which]);
1877 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1878 if (ret / 100 == 2) {
1879 policy[0]->expire_mode = extract_int(cret, 0);
1880 policy[0]->expire_value = extract_int(cret, 1);
1887 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1888 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1889 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1890 struct ExpirePolicy *policy, char *cret)
1893 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1895 if (!cret) return -2;
1896 if (which < 0 || which > 3) return -2;
1897 if (!policy) return -2;
1898 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1899 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1901 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1902 policy->expire_mode, policy->expire_value);
1903 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1908 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1912 if (!cret) return -2;
1913 if (!listing) return -2;
1914 if (*listing) return -2;
1916 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1917 listing, &bytes, cret);
1922 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1924 if (!cret) return -2;
1925 if (!listing) return -2;
1927 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1933 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1934 char **listing, char *cret)
1940 if (!cret) return -2;
1941 if (!mimetype) return -2;
1942 if (!listing) return -2;
1943 if (*listing) return -2;
1945 aaa = malloc(strlen(mimetype) + 13);
1946 if (!aaa) return -1;
1947 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1948 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1949 listing, &bytes, cret);
1956 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1957 const char *listing, char *cret)
1962 if (!cret) return -2;
1963 if (!mimetype) return -2;
1964 if (!listing) return -2;
1966 aaa = malloc(strlen(mimetype) + 13);
1967 if (!aaa) return -1;
1968 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1969 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1977 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1981 if (!cret) return -2;
1982 if (!listing) return -2;
1983 if (*listing) return -2;
1985 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1986 listing, &bytes, cret);
1991 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1993 if (!cret) return -2;
1994 if (!listing) return -2;
1996 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
2002 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2006 if (!cret) return -2;
2007 if (session < 0) return -2;
2009 sprintf(aaa, "REQT %d", session);
2010 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2015 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2019 if (!cret) return -2;
2020 if (msgnum < 0) return -2;
2022 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2023 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2028 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2037 /* New SSL object */
2038 temp_ssl = SSL_new(ssl_ctx);
2040 error_printf("SSL_new failed: %s\n",
2041 ERR_reason_error_string(ERR_get_error()));
2044 /* Pointless flag waving */
2045 #if SSLEAY_VERSION_NUMBER >= 0x0922
2046 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
2049 if (!access(EGD_POOL, F_OK))
2052 if (!RAND_status()) {
2053 error_printf("PRNG not properly seeded\n");
2057 /* Associate network connection with SSL object */
2058 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2059 error_printf("SSL_set_fd failed: %s\n",
2060 ERR_reason_error_string(ERR_get_error()));
2064 if (status_hook != NULL)
2065 status_hook("Requesting encryption...\r");
2067 /* Ready to start SSL/TLS */
2069 CtdlIPC_putline(ipc, "STLS");
2070 CtdlIPC_getline(ipc, buf);
2071 if (buf[0] != '2') {
2072 error_printf("Server can't start TLS: %s\n", buf);
2076 r = CtdlIPCGenericCommand(ipc,
2077 "STLS", NULL, 0, NULL, NULL, cret);
2079 error_printf("Server can't start TLS: %s\n", buf);
2084 /* Do SSL/TLS handshake */
2085 if ((a = SSL_connect(temp_ssl)) < 1) {
2086 error_printf("SSL_connect failed: %s\n",
2087 ERR_reason_error_string(ERR_get_error()));
2091 ipc->ssl = temp_ssl;
2093 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2097 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2098 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2099 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2100 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2106 #endif /* HAVE_OPENSSL */
2111 static void endtls(SSL *ssl)
2122 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2127 if (!address) return -2;
2128 if (!cret) return -2;
2130 aaa = (char *)malloc(strlen(address) + 6);
2131 if (!aaa) return -1;
2133 sprintf(aaa, "QDIR %s", address);
2134 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2141 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2145 if (!cret) return -2;
2146 sprintf(aaa, "IPGM %d", secret);
2147 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2152 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2156 if (!cret) return -2;
2157 if (!mret) return -2;
2158 if (*mret) return -2;
2160 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2181 /* ************************************************************************** */
2182 /* Stuff below this line is not for public consumption */
2183 /* ************************************************************************** */
2186 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2188 if (ipc->network_status_cb) ipc->network_status_cb(1);
2189 #ifdef THREADED_CLIENT
2190 pthread_mutex_lock(&(ipc->mutex));
2195 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2197 #ifdef THREADED_CLIENT
2198 pthread_mutex_unlock(&(ipc->mutex));
2200 if (ipc->network_status_cb) ipc->network_status_cb(0);
2204 /* Read a listing from the server up to 000. Append to dest if it exists */
2205 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2214 length = strlen(ret);
2219 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2220 linelength = strlen(aaa);
2221 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2223 strcpy(&ret[length], aaa);
2224 length += linelength;
2225 strcpy(&ret[length++], "\n");
2233 /* Send a listing to the server; generate the ending 000. */
2234 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2238 text = (char *)malloc(strlen(listing) + 6);
2240 strcpy(text, listing);
2241 while (text[strlen(text) - 1] == '\n')
2242 text[strlen(text) - 1] = '\0';
2243 strcat(text, "\n000");
2244 CtdlIPC_putline(ipc, text);
2248 /* Malloc failed but we are committed to send */
2249 /* This may result in extra blanks at the bottom */
2250 CtdlIPC_putline(ipc, text);
2251 CtdlIPC_putline(ipc, "000");
2257 /* Partial read of file from server */
2258 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2260 register size_t len = 0;
2264 if (!cret) return 0;
2265 if (bytes < 1) return 0;
2268 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2269 CtdlIPC_putline(ipc, aaa);
2270 CtdlIPC_getline(ipc, aaa);
2272 strcpy(cret, &aaa[4]);
2274 len = extract_long(&aaa[4], 0);
2275 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2277 /* I know what I'm doing */
2278 serv_read(ipc, ((char *)(*buf) + offset), len);
2280 /* We have to read regardless */
2281 serv_read(ipc, aaa, len);
2285 CtdlIPC_unlock(ipc);
2291 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2295 if (!cret) return -2;
2296 if (!ipc->downloading) return -2;
2298 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2300 ipc->downloading = 0;
2306 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2310 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2311 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2318 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2319 void (*progress_gauge_callback)
2320 (CtdlIPC*, unsigned long, unsigned long),
2323 register size_t len;
2325 if (!cret) return -1;
2326 if (!buf) return -1;
2327 if (*buf) return -1;
2328 if (!ipc->downloading) return -1;
2331 if (progress_gauge_callback)
2332 progress_gauge_callback(ipc, len, bytes);
2333 while (len < bytes) {
2334 register size_t block;
2336 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2342 if (progress_gauge_callback)
2343 progress_gauge_callback(ipc, len, bytes);
2348 /* READ - pipelined */
2349 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2351 void (*progress_gauge_callback)
2352 (CtdlIPC*, unsigned long, unsigned long),
2355 register size_t len;
2356 register int calls; /* How many calls in the pipeline */
2357 register int i; /* iterator */
2360 if (!cret) return -1;
2361 if (!buf) return -1;
2362 if (*buf) return -1;
2363 if (!ipc->downloading) return -1;
2365 *buf = (void *)realloc(*buf, bytes - resume);
2366 if (!*buf) return -1;
2370 if (progress_gauge_callback)
2371 progress_gauge_callback(ipc, len, bytes);
2373 /* How many calls will be in the pipeline? */
2374 calls = (bytes - resume) / 4096;
2375 if ((bytes - resume) % 4096) calls++;
2377 /* Send all requests at once */
2378 for (i = 0; i < calls; i++) {
2379 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2380 CtdlIPC_putline(ipc, aaa);
2383 /* Receive all responses at once */
2384 for (i = 0; i < calls; i++) {
2385 CtdlIPC_getline(ipc, aaa);
2387 strcpy(cret, &aaa[4]);
2389 len = extract_long(&aaa[4], 0);
2390 /* I know what I'm doing */
2391 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2393 if (progress_gauge_callback)
2394 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2396 CtdlIPC_unlock(ipc);
2402 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2407 if (!cret) return -1;
2408 if (!ipc->uploading) return -1;
2410 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2411 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2418 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2419 void (*progress_gauge_callback)
2420 (CtdlIPC*, unsigned long, unsigned long),
2423 register int ret = -1;
2424 register size_t offset = 0;
2428 FILE *fd = uploadFP;
2431 if (!cret) return -1;
2433 fseek(fd, 0L, SEEK_END);
2437 if (progress_gauge_callback)
2438 progress_gauge_callback(ipc, 0, bytes);
2440 while (offset < bytes) {
2441 register size_t to_write;
2443 /* Read some data in */
2444 to_write = fread(buf, 1, 4096, fd);
2446 if (feof(fd) || ferror(fd)) break;
2448 sprintf(aaa, "WRIT %d", (int)to_write);
2449 CtdlIPC_putline(ipc, aaa);
2450 CtdlIPC_getline(ipc, aaa);
2451 strcpy(cret, &aaa[4]);
2453 if (aaa[0] == '7') {
2454 to_write = extract_long(&aaa[4], 0);
2456 serv_write(ipc, buf, to_write);
2458 if (progress_gauge_callback)
2459 progress_gauge_callback(ipc, offset, bytes);
2460 /* Detect short reads and back up if needed */
2461 /* offset will never be negative anyway */
2462 fseek(fd, (signed)offset, SEEK_SET);
2467 if (progress_gauge_callback)
2468 progress_gauge_callback(ipc, 1, 1);
2471 return (!ferr ? ret : -2);
2476 * Generic command method. This method should handle any server command
2477 * except for CHAT. It takes the following arguments:
2479 * ipc The server to speak with
2480 * command Preformatted command to send to server
2481 * to_send A text or binary file to send to server
2482 * (only sent if server requests it)
2483 * bytes_to_send The number of bytes in to_send (required if
2484 * sending binary, optional if sending listing)
2485 * to_receive Pointer to a NULL pointer, if the server
2486 * sends text or binary we will allocate memory
2487 * for the file and stuff it here
2488 * bytes_to_receive If a file is received, we will store its
2490 * proto_response The protocol response. Caller must provide
2491 * this buffer and ensure that it is at least
2492 * 128 bytes in length.
2494 * This function returns a number equal to the protocol response number,
2495 * -1 if an internal error occurred, -2 if caller provided bad values,
2496 * or 0 - the protocol response number if bad values were found during
2497 * the protocol exchange.
2498 * It stores the protocol response string (minus the number) in
2499 * protocol_response as described above. Some commands send additional
2500 * data in this string.
2502 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2503 const char *command, const char *to_send,
2504 size_t bytes_to_send, char **to_receive,
2505 size_t *bytes_to_receive, char *proto_response)
2511 if (!command) return -2;
2512 if (!proto_response) return -2;
2515 if (ipc->ssl) watch_ssl = 1;
2519 CtdlIPC_putline(ipc, command);
2521 CtdlIPC_getline(ipc, proto_response);
2522 if (proto_response[3] == '*')
2524 ret = atoi(proto_response);
2525 strcpy(proto_response, &proto_response[4]);
2526 switch (ret / 100) {
2527 default: /* Unknown, punt */
2529 case 3: /* MORE_DATA */
2531 /* Don't need to do anything */
2533 case 1: /* LISTING_FOLLOWS */
2534 if (to_receive && !*to_receive && bytes_to_receive) {
2535 *to_receive = CtdlIPCReadListing(ipc, NULL);
2536 } else { /* Drain */
2537 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2541 case 4: /* SEND_LISTING */
2543 CtdlIPCSendListing(ipc, to_send);
2545 /* No listing given, fake it */
2546 CtdlIPC_putline(ipc, "000");
2550 case 6: /* BINARY_FOLLOWS */
2551 if (to_receive && !*to_receive && bytes_to_receive) {
2553 extract_long(proto_response, 0);
2554 *to_receive = (char *)
2555 malloc((size_t)*bytes_to_receive);
2559 serv_read(ipc, *to_receive,
2566 drain = extract_long(proto_response, 0);
2567 while (drain > SIZ) {
2568 serv_read(ipc, buf, SIZ);
2571 serv_read(ipc, buf, drain);
2575 case 7: /* SEND_BINARY */
2576 if (to_send && bytes_to_send) {
2577 serv_write(ipc, to_send, bytes_to_send);
2578 } else if (bytes_to_send) {
2579 /* Fake it, send nulls */
2582 fake = bytes_to_send;
2583 memset(buf, '\0', SIZ);
2584 while (fake > SIZ) {
2585 serv_write(ipc, buf, SIZ);
2588 serv_write(ipc, buf, fake);
2590 } /* else who knows? DANGER WILL ROBINSON */
2592 case 8: /* START_CHAT_MODE */
2593 if (!strncasecmp(command, "CHAT", 4)) {
2594 /* Don't call chatmode with generic! */
2595 CtdlIPC_putline(ipc, "/quit");
2598 /* In this mode we send then receive listing */
2600 CtdlIPCSendListing(ipc, to_send);
2602 /* No listing given, fake it */
2603 CtdlIPC_putline(ipc, "000");
2606 if (to_receive && !*to_receive
2607 && bytes_to_receive) {
2608 *to_receive = CtdlIPCReadListing(ipc, NULL);
2609 } else { /* Drain */
2610 while (CtdlIPC_getline(ipc, buf),
2611 strcmp(buf, "000")) ;
2616 case 9: /* ASYNC_MSG */
2617 /* CtdlIPCDoAsync(ret, proto_response); */
2618 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2624 CtdlIPC_unlock(ipc);
2629 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2631 struct hostent *phe;
2632 struct servent *pse;
2633 struct protoent *ppe;
2634 struct sockaddr_in sin;
2637 memset(&sin, 0, sizeof(sin));
2638 sin.sin_family = AF_INET;
2640 pse = getservbyname(service, protocol);
2642 sin.sin_port = pse->s_port;
2644 else if (atoi(service) > 0) {
2645 sin.sin_port = htons(atoi(service));
2648 sin.sin_port = htons(defaultPort);
2650 phe = gethostbyname(host);
2652 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2653 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2656 if ((ppe = getprotobyname(protocol)) == 0) {
2659 if (!strcmp(protocol, "udp")) {
2665 s = socket(PF_INET, type, ppe->p_proto);
2670 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2678 static int uds_connectsock(int *isLocal, char *sockpath)
2680 struct sockaddr_un addr;
2683 memset(&addr, 0, sizeof(addr));
2684 addr.sun_family = AF_UNIX;
2685 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2687 s = socket(AF_UNIX, SOCK_STREAM, 0);
2692 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2703 * input binary data from socket
2705 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2707 unsigned int len, rlen;
2709 #if defined(HAVE_OPENSSL)
2711 serv_read_ssl(ipc, buf, bytes);
2716 while (len < bytes) {
2717 rlen = read(ipc->sock, &buf[len], bytes - len);
2719 connection_died(ipc, 0);
2728 * send binary to server
2730 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2732 unsigned int bytes_written = 0;
2735 #if defined(HAVE_OPENSSL)
2737 serv_write_ssl(ipc, buf, nbytes);
2741 while (bytes_written < nbytes) {
2742 retval = write(ipc->sock, &buf[bytes_written],
2743 nbytes - bytes_written);
2745 connection_died(ipc, 0);
2748 bytes_written += retval;
2755 * input binary data from encrypted connection
2757 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2763 while (len < bytes) {
2764 if (SSL_want_read(ipc->ssl)) {
2765 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2766 error_printf("SSL_write in serv_read:\n");
2767 ERR_print_errors_fp(stderr);
2770 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2774 errval = SSL_get_error(ipc->ssl, rlen);
2775 if (errval == SSL_ERROR_WANT_READ ||
2776 errval == SSL_ERROR_WANT_WRITE) {
2781 Not sure why we'd want to handle these error codes any differently,
2782 but this definitely isn't the way to handle them. Someone must have
2783 naively assumed that we could fall back to unencrypted communications,
2784 but all it does is just recursively blow the stack.
2785 if (errval == SSL_ERROR_ZERO_RETURN ||
2786 errval == SSL_ERROR_SSL) {
2787 serv_read(ipc, &buf[len], bytes - len);
2791 error_printf("SSL_read in serv_read: %s\n",
2792 ERR_reason_error_string(ERR_peek_error()));
2793 connection_died(ipc, 1);
2802 * send binary to server encrypted
2804 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2806 unsigned int bytes_written = 0;
2810 while (bytes_written < nbytes) {
2811 if (SSL_want_write(ipc->ssl)) {
2812 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2813 error_printf("SSL_read in serv_write:\n");
2814 ERR_print_errors_fp(stderr);
2817 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2818 nbytes - bytes_written);
2822 errval = SSL_get_error(ipc->ssl, retval);
2823 if (errval == SSL_ERROR_WANT_READ ||
2824 errval == SSL_ERROR_WANT_WRITE) {
2828 if (errval == SSL_ERROR_ZERO_RETURN ||
2829 errval == SSL_ERROR_SSL) {
2830 serv_write(ipc, &buf[bytes_written],
2831 nbytes - bytes_written);
2834 error_printf("SSL_write in serv_write: %s\n",
2835 ERR_reason_error_string(ERR_peek_error()));
2836 connection_died(ipc, 1);
2839 bytes_written += retval;
2844 #ifdef THREADED_CLIENT
2845 static void ssl_lock(int mode, int n, const char *file, int line)
2847 if (mode & CRYPTO_LOCK)
2848 pthread_mutex_lock(Critters[n]);
2850 pthread_mutex_unlock(Critters[n]);
2852 #endif /* THREADED_CLIENT */
2855 static void CtdlIPC_init_OpenSSL(void)
2858 SSL_METHOD *ssl_method;
2861 /* already done init */
2870 SSL_load_error_strings();
2871 SSLeay_add_ssl_algorithms();
2873 /* Set up the SSL context in which we will oeprate */
2874 ssl_method = SSLv23_client_method();
2875 ssl_ctx = SSL_CTX_new(ssl_method);
2877 error_printf("SSL_CTX_new failed: %s\n",
2878 ERR_reason_error_string(ERR_get_error()));
2881 /* Any reasonable cipher we can get */
2882 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2883 error_printf("No ciphers available for encryption\n");
2886 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2888 /* Load DH parameters into the context */
2891 error_printf("Can't allocate a DH object: %s\n",
2892 ERR_reason_error_string(ERR_get_error()));
2895 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2896 error_printf("Can't assign DH_P: %s\n",
2897 ERR_reason_error_string(ERR_get_error()));
2901 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2902 error_printf("Can't assign DH_G: %s\n",
2903 ERR_reason_error_string(ERR_get_error()));
2908 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2911 #ifdef THREADED_CLIENT
2912 /* OpenSSL requires callbacks for threaded clients */
2913 CRYPTO_set_locking_callback(ssl_lock);
2914 CRYPTO_set_id_callback(id_callback);
2916 /* OpenSSL requires us to do semaphores for threaded clients */
2917 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2919 perror("malloc failed");
2922 for (a = 0; a < CRYPTO_num_locks(); a++) {
2923 Critters[a] = malloc(sizeof (pthread_mutex_t));
2925 perror("malloc failed");
2928 pthread_mutex_init(Critters[a], NULL);
2931 #endif /* THREADED_CLIENT */
2936 #ifdef THREADED_CLIENT
2937 static unsigned long id_callback(void) {
2938 return (unsigned long)pthread_self();
2940 #endif /* THREADED_CLIENT */
2941 #endif /* HAVE_OPENSSL */
2945 ReadNetworkChunk(CtdlIPC* ipc)
2962 FD_SET(ipc->sock, &read_fd);
2963 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2965 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2969 *(ipc->BufPtr) = '\0';
2970 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2971 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2973 ipc->BufPtr[n]='\0';
2981 if (!(errno == EINTR || errno == EAGAIN))
2982 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2988 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2990 ipc->BufPtr[n]='\0';
2995 connection_died(ipc, 0);
3003 * input string from socket - implemented in terms of serv_read()
3007 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3010 char *aptr, *bptr, *aeptr, *beptr;
3012 // error_printf("---\n");
3015 #if defined(HAVE_OPENSSL)
3018 /* Read one character at a time. */
3020 serv_read(ipc, &buf[i], 1);
3021 if (buf[i] == '\n' || i == (SIZ-1))
3025 /* If we got a long line, discard characters until the newline. */
3027 while (buf[i] != '\n')
3028 serv_read(ipc, &buf[i], 1);
3030 /* Strip the trailing newline (and carriage return, if present) */
3031 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3032 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3037 if (ipc->Buf == NULL)
3040 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3042 ipc->BufPtr = ipc->Buf;
3046 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3047 if (ipc->BufUsed == 0)
3048 ReadNetworkChunk(ipc);
3050 //// if (ipc->BufUsed != 0) while (1)
3056 aeptr = ipc->Buf + ipc->BufSize;
3057 while ((aptr < aeptr) &&
3061 *(bptr++) = *(aptr++);
3062 if ((*aptr == '\n') && (aptr < aeptr))
3064 /* Terminate it right, remove the line breaks */
3065 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3067 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3070 // 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);
3071 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3074 /* is there more in the buffer we need to read later? */
3075 if (ipc->Buf + ipc->BufUsed > aptr)
3082 ipc->BufPtr = ipc->Buf;
3084 // error_printf("----bla6\n");
3087 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3088 else if ((ipc->BufPtr != ipc->Buf) &&
3089 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3091 size_t NewBufSize = ipc->BufSize * 2;
3092 int delta = (ipc->BufPtr - ipc->Buf);
3095 /* if the line would end after our buffer, we should use a bigger buffer. */
3096 NewBuf = (char *)malloc (NewBufSize + 10);
3097 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3099 ipc->Buf = ipc->BufPtr = NewBuf;
3100 ipc->BufUsed -= delta;
3101 ipc->BufSize = NewBufSize;
3103 if (ReadNetworkChunk(ipc) <0)
3105 // error_printf("----bla\n");
3109 /// error_printf("----bl45761%s\nipc->BufUsed");
3111 // error_printf("----bla1\n");
3114 #else /* CHUNKED_READ */
3116 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3120 /* Read one character at a time. */
3122 serv_read(ipc, &buf[i], 1);
3123 if (buf[i] == '\n' || i == (SIZ-1))
3127 /* If we got a long line, discard characters until the newline. */
3129 while (buf[i] != '\n')
3130 serv_read(ipc, &buf[i], 1);
3132 /* Strip the trailing newline (and carriage return, if present) */
3133 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3134 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3138 #endif /* CHUNKED_READ */
3141 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3143 CtdlIPC_getline(ipc, buf);
3147 * send line to server - implemented in terms of serv_write()
3149 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3155 cmd = malloc(len + 2);
3157 /* This requires no extra memory */
3158 serv_write(ipc, buf, len);
3159 serv_write(ipc, "\n", 1);
3161 /* This is network-optimized */
3162 strncpy(cmd, buf, len);
3163 strcpy(cmd + len, "\n");
3164 serv_write(ipc, cmd, len + 1);
3168 ipc->last_command_sent = time(NULL);
3171 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3173 CtdlIPC_putline(ipc, buf);
3180 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3188 ipc = ialloc(CtdlIPC);
3192 #if defined(HAVE_OPENSSL)
3194 CtdlIPC_init_OpenSSL();
3196 #if defined(HAVE_PTHREAD_H)
3197 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3199 ipc->sock = -1; /* Not connected */
3200 ipc->isLocal = 0; /* Not local, of course! */
3201 ipc->downloading = 0;
3203 ipc->last_command_sent = 0L;
3204 ipc->network_status_cb = NULL;
3209 strcpy(cithost, DEFAULT_HOST); /* default host */
3210 strcpy(citport, DEFAULT_PORT); /* default port */
3212 /* Allow caller to supply our values (Windows) */
3213 if (hostbuf && strlen(hostbuf) > 0)
3214 strcpy(cithost, hostbuf);
3215 if (portbuf && strlen(portbuf) > 0)
3216 strcpy(citport, portbuf);
3218 /* Read host/port from command line if present */
3219 for (a = 0; a < argc; ++a) {
3222 } else if (a == 1) {
3223 strcpy(cithost, argv[a]);
3224 } else if (a == 2) {
3225 strcpy(citport, argv[a]);
3227 error_printf("%s: usage: ",argv[0]);
3228 error_printf("%s [host] [port] ",argv[0]);
3235 if ((!strcmp(cithost, "localhost"))
3236 || (!strcmp(cithost, "127.0.0.1"))) {
3240 /* If we're using a unix domain socket we can do a bunch of stuff */
3241 if (!strcmp(cithost, UDS)) {
3242 if (!strcasecmp(citport, DEFAULT_PORT)) {
3243 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3246 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3248 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3249 if (ipc->sock == -1) {
3253 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3254 if (portbuf != NULL) strcpy(portbuf, sockpath);
3258 ipc->sock = connectsock(cithost, citport, "tcp", 504);
3259 if (ipc->sock == -1) {
3263 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3264 if (portbuf != NULL) strcpy(portbuf, citport);
3270 * Disconnect and delete the IPC class (destructor)
3272 void CtdlIPC_delete(CtdlIPC* ipc)
3276 SSL_shutdown(ipc->ssl);
3281 if (ipc->sock > -1) {
3282 shutdown(ipc->sock, 2); /* Close it up */
3285 if (ipc->Buf != NULL)
3294 * Disconnect and delete the IPC class (destructor)
3295 * Also NULLs out the pointer
3297 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3299 CtdlIPC_delete(*pipc);
3305 * return the file descriptor of the server socket so we can select() on it.
3307 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3310 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3317 * return one character
3319 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3322 char CtdlIPC_get(CtdlIPC* ipc)
3327 serv_read(ipc, buf, 1);