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, "time=", 5))
539 mret[0]->time = atol(&aaa[5]);
541 /* Multipart/alternative prefix & suffix strings help
542 * us to determine which part we want to download.
544 else if (!strncasecmp(aaa, "pref=", 5)) {
545 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
546 if (!strcasecmp(multipart_prefix,
547 "multipart/alternative")) {
551 else if (!strncasecmp(aaa, "suff=", 5)) {
552 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
553 if (!strcasecmp(multipart_prefix,
554 "multipart/alternative")) {
559 else if (!strncasecmp(aaa, "part=", 5)) {
560 struct parts *ptr, *chain;
562 ptr = (struct parts *)calloc(1, sizeof (struct parts));
565 /* Fill the buffers for the caller */
566 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
567 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
568 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
569 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
570 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
571 ptr->length = extract_long(&aaa[5], 5);
572 if (!mret[0]->attachments)
573 mret[0]->attachments = ptr;
575 chain = mret[0]->attachments;
581 /* Now handle multipart/alternative */
582 if (multipart_hunting > 0) {
583 if ( (!strcasecmp(ptr->mimetype,
585 || (!strcasecmp(ptr->mimetype,
587 strcpy(mret[0]->mime_chosen,
595 /* Eliminate "text\n" */
596 remove_token(bbb, 0, '\n');
598 /* If doing a MIME thing, pull out the extra headers */
601 if (!strncasecmp(bbb, "Content-type:", 13)) {
602 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
603 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
604 striplt(mret[0]->content_type);
606 /* strip out ";charset=" portion. FIXME do something with
607 * the charset (like... convert it) instead of just throwing
610 if (strstr(mret[0]->content_type, ";") != NULL) {
611 strcpy(strstr(mret[0]->content_type, ";"), "");
615 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
616 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
617 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
618 striplt(mret[0]->mime_chosen);
620 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
621 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
622 strcpy(encoding, &encoding[26]);
625 remove_token(bbb, 0, '\n');
626 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
627 remove_token(bbb, 0, '\n');
634 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
636 int bytes_decoded = 0;
637 ccc = malloc(strlen(bbb) + 32768);
638 if (!strcasecmp(encoding, "base64")) {
639 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
641 else if (!strcasecmp(encoding, "quoted-printable")) {
642 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
644 ccc[bytes_decoded] = 0;
649 /* FIXME: Strip trailing whitespace */
650 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
653 bbb = (char *)realloc(bbb, 1);
663 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
668 if (!cret) return -2;
669 if (!listing) return -2;
670 if (*listing) return -2;
672 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
678 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
682 char *listing = NULL;
685 if (!cret) return -2;
687 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
688 if (ret / 100 == 1) {
691 while (*listing && strlen(listing)) {
692 extract_token(buf, listing, 0, '\n', sizeof buf);
693 remove_token(listing, 0, '\n');
695 case 0: ipc->ServInfo.pid = atoi(buf);
697 case 1: strcpy(ipc->ServInfo.nodename,buf);
699 case 2: strcpy(ipc->ServInfo.humannode,buf);
701 case 3: strcpy(ipc->ServInfo.fqdn,buf);
703 case 4: strcpy(ipc->ServInfo.software,buf);
705 case 5: ipc->ServInfo.rev_level = atoi(buf);
707 case 6: strcpy(ipc->ServInfo.site_location,buf);
709 case 7: strcpy(ipc->ServInfo.sysadm,buf);
711 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
713 case 10: ipc->ServInfo.ok_floors = atoi(buf);
715 case 11: ipc->ServInfo.paging_level = atoi(buf);
717 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
719 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
721 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
723 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
725 case 17: ipc->ServInfo.load_avg = atof(buf);
727 case 18: ipc->ServInfo.worker_avg = atof(buf);
729 case 19: ipc->ServInfo.thread_count = atoi(buf);
731 case 20: ipc->ServInfo.has_sieve = atoi(buf);
733 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
735 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
741 if (listing) free(listing);
747 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
752 if (!cret) return -2;
753 if (!listing) return -2;
754 if (*listing) return -2;
756 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
762 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
764 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
769 if (!cret) return -2;
772 sprintf(aaa, "SLRP %ld", msgnum);
775 sprintf(aaa, "SLRP HIGHEST");
777 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
783 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
788 if (!cret) return -2;
789 if (!username) return -2;
791 aaa = (char *)malloc(strlen(username) + 6);
794 sprintf(aaa, "INVT %s", username);
795 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
802 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
807 if (!cret) return -1;
808 if (!username) return -1;
810 aaa = (char *)malloc(strlen(username) + 6);
812 sprintf(aaa, "KICK %s", username);
813 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
820 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
824 if (!cret) return -2;
825 if (!qret) return -2;
826 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
827 if (!*qret) return -1;
829 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
830 if (ret / 100 == 2) {
831 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
832 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
833 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
834 qret[0]->QRflags = extract_int(cret, 3);
835 qret[0]->QRfloor = extract_int(cret, 4);
836 qret[0]->QRorder = extract_int(cret, 5);
837 qret[0]->QRdefaultview = extract_int(cret, 6);
838 qret[0]->QRflags2 = extract_int(cret, 7);
845 /* set forget to kick all users out of room */
846 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
851 if (!cret) return -2;
852 if (!qret) return -2;
854 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
855 strlen(qret->QRdirname) + 64);
858 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
859 qret->QRname, qret->QRpasswd, qret->QRdirname,
860 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
861 qret->QRdefaultview, qret->QRflags2);
862 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
869 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
871 if (!cret) return -1;
873 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
878 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
883 if (!cret) return -2;
884 if (!username) return -2;
886 aaa = (char *)malloc(strlen(username) + 6);
889 sprintf(aaa, "SETA %s", username);
890 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
897 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, const struct ctdlipcmessage *mr, char *cret)
902 if (!cret) return -2;
905 snprintf(cmd, sizeof cmd,
906 "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
907 mr->anonymous, mr->type, mr->subject, mr->author);
908 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
910 if ((flag == 0) && (subject_required != NULL)) {
911 /* Is the server strongly recommending that the user enter a message subject? */
912 if ((cret[3] != '\0') && (cret[4] != '\0')) {
913 *subject_required = extract_int(&cret[4], 1);
923 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
927 if (!cret) return -2;
928 if (!iret) return -2;
929 if (*iret) return -2;
931 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
936 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
940 if (!cret) return -2;
941 if (!msgnum) return -2;
943 sprintf(aaa, "DELE %ld", msgnum);
944 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
949 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
954 if (!cret) return -2;
955 if (!destroom) return -2;
956 if (!msgnum) return -2;
958 aaa = (char *)malloc(strlen(destroom) + 28);
961 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
962 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
969 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
973 if (!cret) return -2;
975 sprintf(aaa, "KILL %d", for_real);
976 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
981 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
982 const char *password, int floor, char *cret)
987 if (!cret) return -2;
988 if (!roomname) return -2;
991 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
993 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
996 aaa = (char *)malloc(strlen(roomname) + 40);
998 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1001 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1008 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1010 if (!cret) return -2;
1012 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1017 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1023 if (!cret) return -2;
1024 if (!mret) return -2;
1025 if (*mret) return -2;
1026 if (!message) return -2;
1028 aaa = (char *)malloc(strlen(message) + 6);
1029 if (!aaa) return -1;
1031 sprintf(aaa, "MESG %s", message);
1032 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1039 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1041 if (!cret) return -2;
1043 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1048 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1054 if (!cret) return -2;
1055 if (!rret) return -2;
1056 if (*rret) return -2;
1059 aaa = (char *)malloc(strlen(username) + 6);
1061 aaa = (char *)malloc(12);
1062 if (!aaa) return -1;
1065 sprintf(aaa, "GREG %s", username);
1067 sprintf(aaa, "GREG _SELF_");
1068 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1075 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1080 if (!cret) return -2;
1081 if (!username) return -2;
1082 if (axlevel < 0 || axlevel > 7) return -2;
1084 aaa = (char *)malloc(strlen(username) + 17);
1085 if (!aaa) return -1;
1087 sprintf(aaa, "VALI %s|%d", username, axlevel);
1088 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1095 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1099 if (!cret) return -1;
1100 if (!info) return -1;
1102 sprintf(aaa, "EINF %d", for_real);
1103 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1108 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1114 if (!cret) return -1;
1115 if (!listing) return -1;
1116 if (*listing) return -1;
1117 if (!searchstring) return -1;
1119 cmd = malloc(strlen(searchstring) + 10);
1120 sprintf(cmd, "LIST %s", searchstring);
1122 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1129 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1131 if (!cret) return -1;
1132 if (!info) return -1;
1134 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1140 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1144 if (!cret) return -1;
1145 if (!chek) return -1;
1147 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1148 if (ret / 100 == 2) {
1149 chek->newmail = extract_long(cret, 0);
1150 chek->needregis = extract_int(cret, 1);
1151 chek->needvalid = extract_int(cret, 2);
1158 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1163 if (!cret) return -2;
1164 if (!filename) return -2;
1166 aaa = (char *)malloc(strlen(filename) + 6);
1167 if (!aaa) return -1;
1169 sprintf(aaa, "DELF %s", filename);
1170 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1177 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1182 if (!cret) return -2;
1183 if (!filename) return -2;
1184 if (!destroom) return -2;
1186 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1187 if (!aaa) return -1;
1189 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1190 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1197 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1202 if (!cret) return -2;
1203 if (!filename) return -2;
1204 if (!destnode) return -2;
1206 aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1207 if (!aaa) return -1;
1209 sprintf(aaa, "NETF %s|%s", filename, destnode);
1210 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1217 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1222 if (!cret) return -1;
1223 if (!listing) return -1;
1224 if (*listing) return -1;
1226 *stamp = CtdlIPCServerTime(ipc, cret);
1228 *stamp = time(NULL);
1229 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1235 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1237 void (*progress_gauge_callback)
1238 (CtdlIPC*, unsigned long, unsigned long),
1247 if (!cret) return -2;
1248 if (!filename) return -2;
1249 if (!buf) return -2;
1250 if (*buf) return -2;
1251 if (ipc->downloading) return -2;
1253 aaa = (char *)malloc(strlen(filename) + 6);
1254 if (!aaa) return -1;
1256 sprintf(aaa, "OPEN %s", filename);
1257 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1259 if (ret / 100 == 2) {
1260 ipc->downloading = 1;
1261 bytes = extract_long(cret, 0);
1262 last_mod = extract_int(cret, 1);
1263 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1265 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1266 progress_gauge_callback, cret);
1268 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1269 progress_gauge_callback, cret);
1272 ret = CtdlIPCEndDownload(ipc, cret);
1274 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1275 filename, mimetype);
1282 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1284 void (*progress_gauge_callback)
1285 (CtdlIPC*, unsigned long, unsigned long),
1295 if (!cret) return -2;
1296 if (!buf) return -2;
1297 if (*buf) return -2;
1298 if (!part) return -2;
1299 if (!msgnum) return -2;
1300 if (ipc->downloading) return -2;
1302 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1303 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1304 if (ret / 100 == 2) {
1305 ipc->downloading = 1;
1306 bytes = extract_long(cret, 0);
1307 last_mod = extract_int(cret, 1);
1308 extract_token(filename, cret, 2, '|', sizeof filename);
1309 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1310 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1311 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1312 ret = CtdlIPCEndDownload(ipc, cret);
1314 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1315 filename, mimetype);
1322 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1323 void (*progress_gauge_callback)
1324 (CtdlIPC*, unsigned long, unsigned long),
1333 if (!cret) return -1;
1334 if (!buf) return -1;
1335 if (*buf) return -1;
1336 if (!filename) return -1;
1337 if (ipc->downloading) return -1;
1339 aaa = (char *)malloc(strlen(filename) + 6);
1340 if (!aaa) return -1;
1342 sprintf(aaa, "OIMG %s", filename);
1343 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1345 if (ret / 100 == 2) {
1346 ipc->downloading = 1;
1347 bytes = extract_long(cret, 0);
1348 last_mod = extract_int(cret, 1);
1349 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1350 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1351 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1352 ret = CtdlIPCEndDownload(ipc, cret);
1354 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1355 filename, mimetype);
1362 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1364 void (*progress_gauge_callback)
1365 (CtdlIPC*, unsigned long, unsigned long),
1371 char MimeTestBuf[64];
1372 const char *MimeType;
1375 if (!cret) return -1;
1376 if (!save_as) return -1;
1377 if (!comment) return -1;
1378 if (!path) return -1;
1379 if (!*path) return -1;
1380 if (ipc->uploading) return -1;
1382 uploadFP = fopen(path, "r");
1383 if (!uploadFP) return -2;
1385 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1390 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1391 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1392 if (!aaa) return -1;
1394 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1395 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1397 if (ret / 100 == 2) {
1399 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1400 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1408 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1409 const char *save_as,
1410 void (*progress_gauge_callback)
1411 (CtdlIPC*, unsigned long, unsigned long),
1417 char MimeTestBuf[64];
1418 const char *MimeType;
1421 if (!cret) return -1;
1422 if (!save_as) return -1;
1423 if (!path && for_real) return -1;
1424 if (!*path && for_real) return -1;
1425 if (ipc->uploading) return -1;
1427 aaa = (char *)malloc(strlen(save_as) + 17);
1428 if (!aaa) return -1;
1430 uploadFP = fopen(path, "r");
1431 if (!uploadFP) return -2;
1433 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1437 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1439 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1440 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1442 if (ret / 100 == 2 && for_real) {
1444 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1445 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1453 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1458 if (!cret) return -2;
1459 if (!username) return -2;
1461 aaa = (char *)malloc(strlen(username) + 6);
1462 if (!aaa) return -1;
1464 sprintf(aaa, "QUSR %s", username);
1465 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1472 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1476 if (!cret) return -2;
1477 if (!listing) return -2;
1478 if (*listing) return -2;
1480 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1485 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1490 if (!cret) return -2;
1491 if (!name) return -2;
1493 sprintf(aaa, "CFLR %s|%d", name, for_real);
1494 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1500 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1504 if (!cret) return -1;
1505 if (floornum < 0) return -1;
1507 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1508 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1513 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1518 if (!cret) return -2;
1519 if (!floorname) return -2;
1520 if (floornum < 0) return -2;
1522 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1523 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1531 * You only need to fill out hostname, the defaults will be used if any of the
1532 * other fields are not set properly.
1534 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1535 int revision, const char *software_name, const char *hostname,
1541 if (developerid < 0 || clientid < 0 || revision < 0 ||
1545 revision = REV_LEVEL - 600;
1546 software_name = "Citadel (libcitadel)";
1548 if (!hostname) return -2;
1550 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1551 if (!aaa) return -1;
1553 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1554 revision, software_name, hostname);
1555 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1562 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1568 if (!cret) return -2;
1569 if (!username) return -2;
1571 aaa = (char *)malloc(strlen(username) + 8);
1572 if (!aaa) return -1;
1575 sprintf(aaa, "SEXP %s|-", username);
1576 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1579 sprintf(aaa, "SEXP %s||", username);
1580 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1588 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1592 if (!cret) return -2;
1593 if (!listing) return -2;
1594 if (*listing) return -2;
1596 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1601 /* mode is 0 = enable, 1 = disable, 2 = status */
1602 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1606 if (!cret) return -2;
1608 sprintf(aaa, "DEXP %d", mode);
1609 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1614 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1616 if (!cret) return -2;
1617 if (!bio) return -2;
1619 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1625 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1631 if (!cret) return -2;
1632 if (!username) return -2;
1633 if (!listing) return -2;
1634 if (*listing) return -2;
1636 aaa = (char *)malloc(strlen(username) + 6);
1637 if (!aaa) return -1;
1639 sprintf(aaa, "RBIO %s", username);
1640 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1647 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1651 if (!cret) return -2;
1652 if (!listing) return -2;
1653 if (*listing) return -2;
1655 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1660 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1664 if (!cret) return -1;
1666 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1667 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1672 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1676 if (!cret) return -1;
1678 sprintf(aaa, "TERM %d", sid);
1679 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1684 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1686 if (!cret) return -1;
1688 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1693 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1697 if (!cret) return -1;
1699 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1700 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1705 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1711 if (!cret) return -2;
1712 if (!text) return -2;
1713 if (!filename) return -2;
1715 aaa = (char *)malloc(strlen(filename) + 6);
1716 if (!aaa) return -1;
1718 sprintf(aaa, "EMSG %s", filename);
1719 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1726 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1731 if (!cret) return -2;
1732 if (!hostname) return -2;
1734 aaa = (char *)malloc(strlen(hostname) + 6);
1735 if (!aaa) return -1;
1737 sprintf(aaa, "HCHG %s", hostname);
1738 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1745 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1750 if (!cret) return -2;
1751 if (!roomname) return -2;
1753 aaa = (char *)malloc(strlen(roomname) + 6);
1754 if (!aaa) return -1;
1756 sprintf(aaa, "RCHG %s", roomname);
1757 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1764 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1769 if (!cret) return -2;
1770 if (!username) return -2;
1772 aaa = (char *)malloc(strlen(username) + 6);
1773 if (!aaa) return -1;
1775 sprintf(aaa, "UCHG %s", username);
1776 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1783 /* This function returns the actual server time reported, or 0 if error */
1784 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1786 register time_t tret;
1789 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1790 if (ret / 100 == 2) {
1791 tret = extract_long(cret, 0);
1800 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1801 struct ctdluser **uret, char *cret)
1806 if (!cret) return -2;
1807 if (!uret) return -2;
1808 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1809 if (!*uret) return -1;
1811 sprintf(aaa, "AGUP %s", who);
1812 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1814 if (ret / 100 == 2) {
1815 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1816 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1817 uret[0]->flags = extract_int(cret, 2);
1818 uret[0]->timescalled = extract_long(cret, 3);
1819 uret[0]->posted = extract_long(cret, 4);
1820 uret[0]->axlevel = extract_int(cret, 5);
1821 uret[0]->usernum = extract_long(cret, 6);
1822 uret[0]->lastcall = extract_long(cret, 7);
1823 uret[0]->USuserpurge = extract_int(cret, 8);
1830 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1835 if (!cret) return -2;
1836 if (!uret) return -2;
1838 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1839 if (!aaa) return -1;
1841 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1842 uret->fullname, uret->password, uret->flags,
1843 uret->timescalled, uret->posted, uret->axlevel,
1844 uret->usernum, uret->lastcall, uret->USuserpurge);
1845 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1852 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1853 /* caller must free the struct ExpirePolicy */
1854 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1855 struct ExpirePolicy **policy, char *cret)
1857 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1861 if (!cret) return -2;
1862 if (!policy) return -2;
1863 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1864 if (!*policy) return -1;
1865 if (which < 0 || which > 3) return -2;
1867 sprintf(cmd, "GPEX %s", proto[which]);
1868 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1869 if (ret / 100 == 2) {
1870 policy[0]->expire_mode = extract_int(cret, 0);
1871 policy[0]->expire_value = extract_int(cret, 1);
1878 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1879 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1880 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1881 struct ExpirePolicy *policy, char *cret)
1884 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1886 if (!cret) return -2;
1887 if (which < 0 || which > 3) return -2;
1888 if (!policy) return -2;
1889 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1890 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1892 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1893 policy->expire_mode, policy->expire_value);
1894 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1899 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1903 if (!cret) return -2;
1904 if (!listing) return -2;
1905 if (*listing) return -2;
1907 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1908 listing, &bytes, cret);
1913 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1915 if (!cret) return -2;
1916 if (!listing) return -2;
1918 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1924 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1925 char **listing, char *cret)
1931 if (!cret) return -2;
1932 if (!mimetype) return -2;
1933 if (!listing) return -2;
1934 if (*listing) return -2;
1936 aaa = malloc(strlen(mimetype) + 13);
1937 if (!aaa) return -1;
1938 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1939 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1940 listing, &bytes, cret);
1947 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1948 const char *listing, char *cret)
1953 if (!cret) return -2;
1954 if (!mimetype) return -2;
1955 if (!listing) return -2;
1957 aaa = malloc(strlen(mimetype) + 13);
1958 if (!aaa) return -1;
1959 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1960 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1968 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1972 if (!cret) return -2;
1973 if (!listing) return -2;
1974 if (*listing) return -2;
1976 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1977 listing, &bytes, cret);
1982 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1984 if (!cret) return -2;
1985 if (!listing) return -2;
1987 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1993 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1997 if (!cret) return -2;
1998 if (session < 0) return -2;
2000 sprintf(aaa, "REQT %d", session);
2001 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2006 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2010 if (!cret) return -2;
2011 if (msgnum < 0) return -2;
2013 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2014 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2019 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2028 /* New SSL object */
2029 temp_ssl = SSL_new(ssl_ctx);
2031 error_printf("SSL_new failed: %s\n",
2032 ERR_reason_error_string(ERR_get_error()));
2035 /* Pointless flag waving */
2036 #if SSLEAY_VERSION_NUMBER >= 0x0922
2037 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
2040 if (!access(EGD_POOL, F_OK))
2043 if (!RAND_status()) {
2044 error_printf("PRNG not properly seeded\n");
2048 /* Associate network connection with SSL object */
2049 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2050 error_printf("SSL_set_fd failed: %s\n",
2051 ERR_reason_error_string(ERR_get_error()));
2055 if (status_hook != NULL)
2056 status_hook("Requesting encryption...\r");
2058 /* Ready to start SSL/TLS */
2060 CtdlIPC_putline(ipc, "STLS");
2061 CtdlIPC_getline(ipc, buf);
2062 if (buf[0] != '2') {
2063 error_printf("Server can't start TLS: %s\n", buf);
2067 r = CtdlIPCGenericCommand(ipc,
2068 "STLS", NULL, 0, NULL, NULL, cret);
2070 error_printf("Server can't start TLS: %s\n", buf);
2075 /* Do SSL/TLS handshake */
2076 if ((a = SSL_connect(temp_ssl)) < 1) {
2077 error_printf("SSL_connect failed: %s\n",
2078 ERR_reason_error_string(ERR_get_error()));
2082 ipc->ssl = temp_ssl;
2084 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2088 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2089 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2090 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2091 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2097 #endif /* HAVE_OPENSSL */
2102 static void endtls(SSL *ssl)
2113 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2118 if (!address) return -2;
2119 if (!cret) return -2;
2121 aaa = (char *)malloc(strlen(address) + 6);
2122 if (!aaa) return -1;
2124 sprintf(aaa, "QDIR %s", address);
2125 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2132 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2136 if (!cret) return -2;
2137 sprintf(aaa, "IPGM %d", secret);
2138 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2143 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2147 if (!cret) return -2;
2148 if (!mret) return -2;
2149 if (*mret) return -2;
2151 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2172 /* ************************************************************************** */
2173 /* Stuff below this line is not for public consumption */
2174 /* ************************************************************************** */
2177 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2179 if (ipc->network_status_cb) ipc->network_status_cb(1);
2180 #ifdef THREADED_CLIENT
2181 pthread_mutex_lock(&(ipc->mutex));
2186 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2188 #ifdef THREADED_CLIENT
2189 pthread_mutex_unlock(&(ipc->mutex));
2191 if (ipc->network_status_cb) ipc->network_status_cb(0);
2195 /* Read a listing from the server up to 000. Append to dest if it exists */
2196 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2205 length = strlen(ret);
2210 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2211 linelength = strlen(aaa);
2212 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2214 strcpy(&ret[length], aaa);
2215 length += linelength;
2216 strcpy(&ret[length++], "\n");
2224 /* Send a listing to the server; generate the ending 000. */
2225 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2229 text = (char *)malloc(strlen(listing) + 6);
2231 strcpy(text, listing);
2232 while (text[strlen(text) - 1] == '\n')
2233 text[strlen(text) - 1] = '\0';
2234 strcat(text, "\n000");
2235 CtdlIPC_putline(ipc, text);
2239 /* Malloc failed but we are committed to send */
2240 /* This may result in extra blanks at the bottom */
2241 CtdlIPC_putline(ipc, text);
2242 CtdlIPC_putline(ipc, "000");
2248 /* Partial read of file from server */
2249 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2251 register size_t len = 0;
2255 if (!cret) return 0;
2256 if (bytes < 1) return 0;
2259 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2260 CtdlIPC_putline(ipc, aaa);
2261 CtdlIPC_getline(ipc, aaa);
2263 strcpy(cret, &aaa[4]);
2265 len = extract_long(&aaa[4], 0);
2266 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2268 /* I know what I'm doing */
2269 serv_read(ipc, ((char *)(*buf) + offset), len);
2271 /* We have to read regardless */
2272 serv_read(ipc, aaa, len);
2276 CtdlIPC_unlock(ipc);
2282 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2286 if (!cret) return -2;
2287 if (!ipc->downloading) return -2;
2289 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2291 ipc->downloading = 0;
2297 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2301 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2302 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2309 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2310 void (*progress_gauge_callback)
2311 (CtdlIPC*, unsigned long, unsigned long),
2314 register size_t len;
2316 if (!cret) return -1;
2317 if (!buf) return -1;
2318 if (*buf) return -1;
2319 if (!ipc->downloading) return -1;
2322 if (progress_gauge_callback)
2323 progress_gauge_callback(ipc, len, bytes);
2324 while (len < bytes) {
2325 register size_t block;
2327 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2333 if (progress_gauge_callback)
2334 progress_gauge_callback(ipc, len, bytes);
2339 /* READ - pipelined */
2340 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2342 void (*progress_gauge_callback)
2343 (CtdlIPC*, unsigned long, unsigned long),
2346 register size_t len;
2347 register int calls; /* How many calls in the pipeline */
2348 register int i; /* iterator */
2351 if (!cret) return -1;
2352 if (!buf) return -1;
2353 if (*buf) return -1;
2354 if (!ipc->downloading) return -1;
2356 *buf = (void *)realloc(*buf, bytes - resume);
2357 if (!*buf) return -1;
2361 if (progress_gauge_callback)
2362 progress_gauge_callback(ipc, len, bytes);
2364 /* How many calls will be in the pipeline? */
2365 calls = (bytes - resume) / 4096;
2366 if ((bytes - resume) % 4096) calls++;
2368 /* Send all requests at once */
2369 for (i = 0; i < calls; i++) {
2370 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2371 CtdlIPC_putline(ipc, aaa);
2374 /* Receive all responses at once */
2375 for (i = 0; i < calls; i++) {
2376 CtdlIPC_getline(ipc, aaa);
2378 strcpy(cret, &aaa[4]);
2380 len = extract_long(&aaa[4], 0);
2381 /* I know what I'm doing */
2382 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2384 if (progress_gauge_callback)
2385 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2387 CtdlIPC_unlock(ipc);
2393 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2398 if (!cret) return -1;
2399 if (!ipc->uploading) return -1;
2401 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2402 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2409 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2410 void (*progress_gauge_callback)
2411 (CtdlIPC*, unsigned long, unsigned long),
2414 register int ret = -1;
2415 register size_t offset = 0;
2419 FILE *fd = uploadFP;
2422 if (!cret) return -1;
2424 fseek(fd, 0L, SEEK_END);
2428 if (progress_gauge_callback)
2429 progress_gauge_callback(ipc, 0, bytes);
2431 while (offset < bytes) {
2432 register size_t to_write;
2434 /* Read some data in */
2435 to_write = fread(buf, 1, 4096, fd);
2437 if (feof(fd) || ferror(fd)) break;
2439 sprintf(aaa, "WRIT %d", (int)to_write);
2440 CtdlIPC_putline(ipc, aaa);
2441 CtdlIPC_getline(ipc, aaa);
2442 strcpy(cret, &aaa[4]);
2444 if (aaa[0] == '7') {
2445 to_write = extract_long(&aaa[4], 0);
2447 serv_write(ipc, buf, to_write);
2449 if (progress_gauge_callback)
2450 progress_gauge_callback(ipc, offset, bytes);
2451 /* Detect short reads and back up if needed */
2452 /* offset will never be negative anyway */
2453 fseek(fd, (signed)offset, SEEK_SET);
2458 if (progress_gauge_callback)
2459 progress_gauge_callback(ipc, 1, 1);
2462 return (!ferr ? ret : -2);
2467 * Generic command method. This method should handle any server command
2468 * except for CHAT. It takes the following arguments:
2470 * ipc The server to speak with
2471 * command Preformatted command to send to server
2472 * to_send A text or binary file to send to server
2473 * (only sent if server requests it)
2474 * bytes_to_send The number of bytes in to_send (required if
2475 * sending binary, optional if sending listing)
2476 * to_receive Pointer to a NULL pointer, if the server
2477 * sends text or binary we will allocate memory
2478 * for the file and stuff it here
2479 * bytes_to_receive If a file is received, we will store its
2481 * proto_response The protocol response. Caller must provide
2482 * this buffer and ensure that it is at least
2483 * 128 bytes in length.
2485 * This function returns a number equal to the protocol response number,
2486 * -1 if an internal error occurred, -2 if caller provided bad values,
2487 * or 0 - the protocol response number if bad values were found during
2488 * the protocol exchange.
2489 * It stores the protocol response string (minus the number) in
2490 * protocol_response as described above. Some commands send additional
2491 * data in this string.
2493 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2494 const char *command, const char *to_send,
2495 size_t bytes_to_send, char **to_receive,
2496 size_t *bytes_to_receive, char *proto_response)
2502 if (!command) return -2;
2503 if (!proto_response) return -2;
2506 if (ipc->ssl) watch_ssl = 1;
2510 CtdlIPC_putline(ipc, command);
2512 CtdlIPC_getline(ipc, proto_response);
2513 if (proto_response[3] == '*')
2515 ret = atoi(proto_response);
2516 strcpy(proto_response, &proto_response[4]);
2517 switch (ret / 100) {
2518 default: /* Unknown, punt */
2520 case 3: /* MORE_DATA */
2522 /* Don't need to do anything */
2524 case 1: /* LISTING_FOLLOWS */
2525 if (to_receive && !*to_receive && bytes_to_receive) {
2526 *to_receive = CtdlIPCReadListing(ipc, NULL);
2527 } else { /* Drain */
2528 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2532 case 4: /* SEND_LISTING */
2534 CtdlIPCSendListing(ipc, to_send);
2536 /* No listing given, fake it */
2537 CtdlIPC_putline(ipc, "000");
2541 case 6: /* BINARY_FOLLOWS */
2542 if (to_receive && !*to_receive && bytes_to_receive) {
2544 extract_long(proto_response, 0);
2545 *to_receive = (char *)
2546 malloc((size_t)*bytes_to_receive);
2550 serv_read(ipc, *to_receive,
2557 drain = extract_long(proto_response, 0);
2558 while (drain > SIZ) {
2559 serv_read(ipc, buf, SIZ);
2562 serv_read(ipc, buf, drain);
2566 case 7: /* SEND_BINARY */
2567 if (to_send && bytes_to_send) {
2568 serv_write(ipc, to_send, bytes_to_send);
2569 } else if (bytes_to_send) {
2570 /* Fake it, send nulls */
2573 fake = bytes_to_send;
2574 memset(buf, '\0', SIZ);
2575 while (fake > SIZ) {
2576 serv_write(ipc, buf, SIZ);
2579 serv_write(ipc, buf, fake);
2581 } /* else who knows? DANGER WILL ROBINSON */
2583 case 8: /* START_CHAT_MODE */
2584 if (!strncasecmp(command, "CHAT", 4)) {
2585 /* Don't call chatmode with generic! */
2586 CtdlIPC_putline(ipc, "/quit");
2589 /* In this mode we send then receive listing */
2591 CtdlIPCSendListing(ipc, to_send);
2593 /* No listing given, fake it */
2594 CtdlIPC_putline(ipc, "000");
2597 if (to_receive && !*to_receive
2598 && bytes_to_receive) {
2599 *to_receive = CtdlIPCReadListing(ipc, NULL);
2600 } else { /* Drain */
2601 while (CtdlIPC_getline(ipc, buf),
2602 strcmp(buf, "000")) ;
2607 case 9: /* ASYNC_MSG */
2608 /* CtdlIPCDoAsync(ret, proto_response); */
2609 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2615 CtdlIPC_unlock(ipc);
2620 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2622 struct hostent *phe;
2623 struct servent *pse;
2624 struct protoent *ppe;
2625 struct sockaddr_in sin;
2628 memset(&sin, 0, sizeof(sin));
2629 sin.sin_family = AF_INET;
2631 pse = getservbyname(service, protocol);
2633 sin.sin_port = pse->s_port;
2635 else if (atoi(service) > 0) {
2636 sin.sin_port = htons(atoi(service));
2639 sin.sin_port = htons(defaultPort);
2641 phe = gethostbyname(host);
2643 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2644 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2647 if ((ppe = getprotobyname(protocol)) == 0) {
2650 if (!strcmp(protocol, "udp")) {
2656 s = socket(PF_INET, type, ppe->p_proto);
2661 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2669 static int uds_connectsock(int *isLocal, char *sockpath)
2671 struct sockaddr_un addr;
2674 memset(&addr, 0, sizeof(addr));
2675 addr.sun_family = AF_UNIX;
2676 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2678 s = socket(AF_UNIX, SOCK_STREAM, 0);
2683 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2694 * input binary data from socket
2696 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2698 unsigned int len, rlen;
2700 #if defined(HAVE_OPENSSL)
2702 serv_read_ssl(ipc, buf, bytes);
2707 while (len < bytes) {
2708 rlen = read(ipc->sock, &buf[len], bytes - len);
2710 connection_died(ipc, 0);
2719 * send binary to server
2721 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2723 unsigned int bytes_written = 0;
2726 #if defined(HAVE_OPENSSL)
2728 serv_write_ssl(ipc, buf, nbytes);
2732 while (bytes_written < nbytes) {
2733 retval = write(ipc->sock, &buf[bytes_written],
2734 nbytes - bytes_written);
2736 connection_died(ipc, 0);
2739 bytes_written += retval;
2746 * input binary data from encrypted connection
2748 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2754 while (len < bytes) {
2755 if (SSL_want_read(ipc->ssl)) {
2756 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2757 error_printf("SSL_write in serv_read:\n");
2758 ERR_print_errors_fp(stderr);
2761 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2765 errval = SSL_get_error(ipc->ssl, rlen);
2766 if (errval == SSL_ERROR_WANT_READ ||
2767 errval == SSL_ERROR_WANT_WRITE) {
2772 Not sure why we'd want to handle these error codes any differently,
2773 but this definitely isn't the way to handle them. Someone must have
2774 naively assumed that we could fall back to unencrypted communications,
2775 but all it does is just recursively blow the stack.
2776 if (errval == SSL_ERROR_ZERO_RETURN ||
2777 errval == SSL_ERROR_SSL) {
2778 serv_read(ipc, &buf[len], bytes - len);
2782 error_printf("SSL_read in serv_read: %s\n",
2783 ERR_reason_error_string(ERR_peek_error()));
2784 connection_died(ipc, 1);
2793 * send binary to server encrypted
2795 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2797 unsigned int bytes_written = 0;
2801 while (bytes_written < nbytes) {
2802 if (SSL_want_write(ipc->ssl)) {
2803 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2804 error_printf("SSL_read in serv_write:\n");
2805 ERR_print_errors_fp(stderr);
2808 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2809 nbytes - bytes_written);
2813 errval = SSL_get_error(ipc->ssl, retval);
2814 if (errval == SSL_ERROR_WANT_READ ||
2815 errval == SSL_ERROR_WANT_WRITE) {
2819 if (errval == SSL_ERROR_ZERO_RETURN ||
2820 errval == SSL_ERROR_SSL) {
2821 serv_write(ipc, &buf[bytes_written],
2822 nbytes - bytes_written);
2825 error_printf("SSL_write in serv_write: %s\n",
2826 ERR_reason_error_string(ERR_peek_error()));
2827 connection_died(ipc, 1);
2830 bytes_written += retval;
2835 #ifdef THREADED_CLIENT
2836 static void ssl_lock(int mode, int n, const char *file, int line)
2838 if (mode & CRYPTO_LOCK)
2839 pthread_mutex_lock(Critters[n]);
2841 pthread_mutex_unlock(Critters[n]);
2843 #endif /* THREADED_CLIENT */
2846 static void CtdlIPC_init_OpenSSL(void)
2849 SSL_METHOD *ssl_method;
2852 /* already done init */
2861 SSL_load_error_strings();
2862 SSLeay_add_ssl_algorithms();
2864 /* Set up the SSL context in which we will oeprate */
2865 ssl_method = SSLv23_client_method();
2866 ssl_ctx = SSL_CTX_new(ssl_method);
2868 error_printf("SSL_CTX_new failed: %s\n",
2869 ERR_reason_error_string(ERR_get_error()));
2872 /* Any reasonable cipher we can get */
2873 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2874 error_printf("No ciphers available for encryption\n");
2877 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2879 /* Load DH parameters into the context */
2882 error_printf("Can't allocate a DH object: %s\n",
2883 ERR_reason_error_string(ERR_get_error()));
2886 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2887 error_printf("Can't assign DH_P: %s\n",
2888 ERR_reason_error_string(ERR_get_error()));
2892 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2893 error_printf("Can't assign DH_G: %s\n",
2894 ERR_reason_error_string(ERR_get_error()));
2899 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2902 #ifdef THREADED_CLIENT
2903 /* OpenSSL requires callbacks for threaded clients */
2904 CRYPTO_set_locking_callback(ssl_lock);
2905 CRYPTO_set_id_callback(id_callback);
2907 /* OpenSSL requires us to do semaphores for threaded clients */
2908 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2910 perror("malloc failed");
2913 for (a = 0; a < CRYPTO_num_locks(); a++) {
2914 Critters[a] = malloc(sizeof (pthread_mutex_t));
2916 perror("malloc failed");
2919 pthread_mutex_init(Critters[a], NULL);
2922 #endif /* THREADED_CLIENT */
2927 #ifdef THREADED_CLIENT
2928 static unsigned long id_callback(void) {
2929 return (unsigned long)pthread_self();
2931 #endif /* THREADED_CLIENT */
2932 #endif /* HAVE_OPENSSL */
2936 ReadNetworkChunk(CtdlIPC* ipc)
2953 FD_SET(ipc->sock, &read_fd);
2954 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2956 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2960 *(ipc->BufPtr) = '\0';
2961 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2962 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2964 ipc->BufPtr[n]='\0';
2972 if (!(errno == EINTR || errno == EAGAIN))
2973 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2979 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2981 ipc->BufPtr[n]='\0';
2986 connection_died(ipc, 0);
2994 * input string from socket - implemented in terms of serv_read()
2998 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3001 char *aptr, *bptr, *aeptr, *beptr;
3003 // error_printf("---\n");
3006 #if defined(HAVE_OPENSSL)
3009 /* Read one character at a time. */
3011 serv_read(ipc, &buf[i], 1);
3012 if (buf[i] == '\n' || i == (SIZ-1))
3016 /* If we got a long line, discard characters until the newline. */
3018 while (buf[i] != '\n')
3019 serv_read(ipc, &buf[i], 1);
3021 /* Strip the trailing newline (and carriage return, if present) */
3022 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3023 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3028 if (ipc->Buf == NULL)
3031 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3033 ipc->BufPtr = ipc->Buf;
3037 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3038 if (ipc->BufUsed == 0)
3039 ReadNetworkChunk(ipc);
3041 //// if (ipc->BufUsed != 0) while (1)
3047 aeptr = ipc->Buf + ipc->BufSize;
3048 while ((aptr < aeptr) &&
3052 *(bptr++) = *(aptr++);
3053 if ((*aptr == '\n') && (aptr < aeptr))
3055 /* Terminate it right, remove the line breaks */
3056 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3058 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3061 // 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);
3062 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3065 /* is there more in the buffer we need to read later? */
3066 if (ipc->Buf + ipc->BufUsed > aptr)
3073 ipc->BufPtr = ipc->Buf;
3075 // error_printf("----bla6\n");
3078 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3079 else if ((ipc->BufPtr != ipc->Buf) &&
3080 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3082 size_t NewBufSize = ipc->BufSize * 2;
3083 int delta = (ipc->BufPtr - ipc->Buf);
3086 /* if the line would end after our buffer, we should use a bigger buffer. */
3087 NewBuf = (char *)malloc (NewBufSize + 10);
3088 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3090 ipc->Buf = ipc->BufPtr = NewBuf;
3091 ipc->BufUsed -= delta;
3092 ipc->BufSize = NewBufSize;
3094 if (ReadNetworkChunk(ipc) <0)
3096 // error_printf("----bla\n");
3100 /// error_printf("----bl45761%s\nipc->BufUsed");
3102 // error_printf("----bla1\n");
3105 #else /* CHUNKED_READ */
3107 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3111 /* Read one character at a time. */
3113 serv_read(ipc, &buf[i], 1);
3114 if (buf[i] == '\n' || i == (SIZ-1))
3118 /* If we got a long line, discard characters until the newline. */
3120 while (buf[i] != '\n')
3121 serv_read(ipc, &buf[i], 1);
3123 /* Strip the trailing newline (and carriage return, if present) */
3124 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3125 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3129 #endif /* CHUNKED_READ */
3132 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3134 CtdlIPC_getline(ipc, buf);
3138 * send line to server - implemented in terms of serv_write()
3140 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3146 cmd = malloc(len + 2);
3148 /* This requires no extra memory */
3149 serv_write(ipc, buf, len);
3150 serv_write(ipc, "\n", 1);
3152 /* This is network-optimized */
3153 strncpy(cmd, buf, len);
3154 strcpy(cmd + len, "\n");
3155 serv_write(ipc, cmd, len + 1);
3159 ipc->last_command_sent = time(NULL);
3162 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3164 CtdlIPC_putline(ipc, buf);
3171 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3179 ipc = ialloc(CtdlIPC);
3183 #if defined(HAVE_OPENSSL)
3185 CtdlIPC_init_OpenSSL();
3187 #if defined(HAVE_PTHREAD_H)
3188 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3190 ipc->sock = -1; /* Not connected */
3191 ipc->isLocal = 0; /* Not local, of course! */
3192 ipc->downloading = 0;
3194 ipc->last_command_sent = 0L;
3195 ipc->network_status_cb = NULL;
3200 strcpy(cithost, DEFAULT_HOST); /* default host */
3201 strcpy(citport, DEFAULT_PORT); /* default port */
3203 /* Allow caller to supply our values (Windows) */
3204 if (hostbuf && strlen(hostbuf) > 0)
3205 strcpy(cithost, hostbuf);
3206 if (portbuf && strlen(portbuf) > 0)
3207 strcpy(citport, portbuf);
3209 /* Read host/port from command line if present */
3210 for (a = 0; a < argc; ++a) {
3213 } else if (a == 1) {
3214 strcpy(cithost, argv[a]);
3215 } else if (a == 2) {
3216 strcpy(citport, argv[a]);
3218 error_printf("%s: usage: ",argv[0]);
3219 error_printf("%s [host] [port] ",argv[0]);
3226 if ((!strcmp(cithost, "localhost"))
3227 || (!strcmp(cithost, "127.0.0.1"))) {
3231 /* If we're using a unix domain socket we can do a bunch of stuff */
3232 if (!strcmp(cithost, UDS)) {
3233 if (!strcasecmp(citport, DEFAULT_PORT)) {
3234 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3237 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3239 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3240 if (ipc->sock == -1) {
3244 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3245 if (portbuf != NULL) strcpy(portbuf, sockpath);
3249 ipc->sock = connectsock(cithost, citport, "tcp", 504);
3250 if (ipc->sock == -1) {
3254 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3255 if (portbuf != NULL) strcpy(portbuf, citport);
3261 * Disconnect and delete the IPC class (destructor)
3263 void CtdlIPC_delete(CtdlIPC* ipc)
3267 SSL_shutdown(ipc->ssl);
3272 if (ipc->sock > -1) {
3273 shutdown(ipc->sock, 2); /* Close it up */
3276 if (ipc->Buf != NULL)
3285 * Disconnect and delete the IPC class (destructor)
3286 * Also NULLs out the pointer
3288 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3290 CtdlIPC_delete(*pipc);
3296 * return the file descriptor of the server socket so we can select() on it.
3298 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3301 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3308 * return one character
3310 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3313 char CtdlIPC_get(CtdlIPC* ipc)
3318 serv_read(ipc, buf, 1);