2 * Copyright (c) 1987-2012 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
14 #if TIME_WITH_SYS_TIME
15 # include <sys/time.h>
19 # include <sys/time.h>
26 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <netinet/in.h>
39 #ifdef THREADED_CLIENT
42 #include <libcitadel.h>
44 #include "citadel_ipc.h"
45 #include "citadel_decls.h"
46 #include "citadel_dirs.h"
47 #ifdef THREADED_CLIENT
48 pthread_mutex_t rwlock;
52 static SSL_CTX *ssl_ctx;
55 #ifdef THREADED_CLIENT
56 pthread_mutex_t **Critters; /* Things that need locking */
57 #endif /* THREADED_CLIENT */
59 #endif /* HAVE_OPENSSL */
62 #define INADDR_NONE 0xffffffff
65 static void (*status_hook)(char *s) = NULL;
67 void setCryptoStatusHook(void (*hook)(char *s)) {
71 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
72 ipc->network_status_cb = hook;
76 char instant_msgs = 0;
79 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
80 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
82 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
83 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
84 static void endtls(SSL *ssl);
85 #ifdef THREADED_CLIENT
86 static unsigned long id_callback(void);
87 #endif /* THREADED_CLIENT */
88 #endif /* HAVE_OPENSSL */
89 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
90 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
94 const char *svn_revision(void);
97 * Does nothing. The server should always return 200.
99 int CtdlIPCNoop(CtdlIPC *ipc)
103 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
108 * Does nothing interesting. The server should always return 200
109 * along with your string.
111 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
117 if (!cret) return -2;
119 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
122 sprintf(aaa, "ECHO %s", arg);
123 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
130 * Asks the server to close the connecction.
131 * Should always return 200.
133 int CtdlIPCQuit(CtdlIPC *ipc)
135 register int ret = 221; /* Default to successful quit */
139 if (ipc->sock > -1) {
140 CtdlIPC_putline(ipc, "QUIT");
141 CtdlIPC_getline(ipc, aaa);
146 SSL_shutdown(ipc->ssl);
150 shutdown(ipc->sock, 2); /* Close connection; we're dead */
158 * Asks the server to log out. Should always return 200, even if no user
159 * was logged in. The user will not be logged in after this!
161 int CtdlIPCLogout(CtdlIPC *ipc)
167 CtdlIPC_putline(ipc, "LOUT");
168 CtdlIPC_getline(ipc, aaa);
176 * First stage of authentication - pass the username. Returns 300 if the
177 * username is able to log in, with the username correctly spelled in cret.
178 * Returns various 500 error codes if the user doesn't exist, etc.
180 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
185 if (!username) return -2;
186 if (!cret) return -2;
188 aaa = (char *)malloc((size_t)(strlen(username) + 6));
191 sprintf(aaa, "USER %s", username);
192 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
199 * Second stage of authentication - provide password. The server returns
200 * 200 and several arguments in cret relating to the user's account.
202 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
207 if (!passwd) return -2;
208 if (!cret) return -2;
210 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
213 sprintf(aaa, "PASS %s", passwd);
214 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
221 * Second stage of authentication - provide password. The server returns
222 * 200 and several arguments in cret relating to the user's account.
224 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
229 if (!response) return -2;
230 if (!cret) return -2;
232 aaa = (char *)malloc((size_t)(strlen(response) + 6));
235 sprintf(aaa, "PAS2 %s", response);
236 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
243 * Create a new user. This returns 200 plus the same arguments as TryPassword
244 * if selfservice is nonzero, unless there was a problem creating the account.
245 * If selfservice is zero, creates a new user but does not log out the existing
246 * user - intended for use by system administrators to create accounts on
247 * behalf of other users.
249 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
254 if (!username) return -2;
255 if (!cret) return -2;
257 aaa = (char *)malloc((size_t)(strlen(username) + 6));
260 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
261 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
268 * Changes the user's password. Returns 200 if changed, errors otherwise.
270 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
275 if (!passwd) return -2;
276 if (!cret) return -2;
278 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
281 sprintf(aaa, "SETP %s", passwd);
282 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
289 /* Caller must free the march list */
290 /* Room types are defined in enum RoomList; keep these in sync! */
291 /* floor is -1 for all, or floornum */
292 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
295 struct march *march = NULL;
296 static char *proto[] =
297 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
302 if (!listing) return -2;
303 if (*listing) return -2; /* Free the listing first */
304 if (!cret) return -2;
305 /* if (which < 0 || which > 4) return -2; */
306 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
308 sprintf(aaa, "%s %d", proto[which], floor);
309 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
310 if (ret / 100 == 1) {
313 while (bbb && strlen(bbb)) {
316 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
318 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
319 mptr = (struct march *) malloc(sizeof (struct march));
322 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
323 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
324 mptr->march_floor = (char) extract_int(aaa, 2);
325 mptr->march_order = (char) extract_int(aaa, 3);
326 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
327 mptr->march_access = (char) extract_int(aaa, 5);
334 while (mptr2->next != NULL)
348 /* Caller must free the struct ctdluser; caller may pass an existing one */
349 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
353 if (!cret) return -2;
354 if (!uret) return -2;
355 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
356 if (!*uret) return -1;
358 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
359 if (ret / 100 == 2) {
360 uret[0]->flags = extract_int(cret, 2);
367 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
371 if (!uret) return -2;
372 if (!cret) return -2;
378 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
383 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
388 if (!oldname) return -2;
389 if (!newname) return -2;
390 if (!cret) return -2;
392 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
393 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
399 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
400 struct ctdlipcroom **rret, char *cret)
405 if (!cret) return -2;
406 if (!rret) return -2;
407 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
408 if (!*rret) return -1;
411 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
416 sprintf(aaa, "GOTO %s|%s", room, passwd);
418 aaa = (char *)malloc(strlen(room) + 6);
423 sprintf(aaa, "GOTO %s", room);
425 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
426 if (ret / 100 == 2) {
427 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
428 rret[0]->RRunread = extract_long(cret, 1);
429 rret[0]->RRtotal = extract_long(cret, 2);
430 rret[0]->RRinfoupdated = extract_int(cret, 3);
431 rret[0]->RRflags = extract_int(cret, 4);
432 rret[0]->RRhighest = extract_long(cret, 5);
433 rret[0]->RRlastread = extract_long(cret, 6);
434 rret[0]->RRismailbox = extract_int(cret, 7);
435 rret[0]->RRaide = extract_int(cret, 8);
436 rret[0]->RRnewmail = extract_long(cret, 9);
437 rret[0]->RRfloor = extract_int(cret, 10);
438 rret[0]->RRcurrentview = extract_int(cret, 11);
439 rret[0]->RRdefaultview = extract_int(cret, 12);
440 /* position 13 is a trash folder flag ... irrelevant in this client */
441 rret[0]->RRflags2 = extract_int(cret, 14);
452 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
453 /* whicharg is number of messages, applies to last, first, gt, lt */
454 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
455 const char *mtemplate, unsigned long **mret, char *cret)
458 register unsigned long count = 0;
459 static char *proto[] =
460 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
465 if (!cret) return -2;
466 if (!mret) return -2;
467 if (*mret) return -2;
468 if (which < 0 || which > 6) return -2;
471 sprintf(aaa, "MSGS %s||%d", proto[which],
472 (mtemplate) ? 1 : 0);
474 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
475 (mtemplate) ? 1 : 0);
476 if (mtemplate) count = strlen(mtemplate);
477 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
481 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
484 while (bbb && strlen(bbb)) {
485 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
486 remove_token(bbb, 0, '\n');
487 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
488 sizeof (unsigned long)));
490 (*mret)[count++] = atol(aaa);
502 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
503 struct ctdlipcmessage **mret, char *cret)
509 int multipart_hunting = 0;
510 char multipart_prefix[128];
513 if (!cret) return -1;
514 if (!mret) return -1;
515 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
516 if (!*mret) return -1;
517 if (!msgnum) return -1;
519 strcpy(encoding, "");
520 strcpy(mret[0]->content_type, "");
521 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
522 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
523 if (ret / 100 == 1) {
525 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
526 while (strlen(bbb) > 4 && bbb[4] == '=') {
527 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
528 remove_token(bbb, 0, '\n');
530 if (!strncasecmp(aaa, "nhdr=yes", 8))
532 else if (!strncasecmp(aaa, "from=", 5))
533 safestrncpy(mret[0]->author, &aaa[5], SIZ);
534 else if (!strncasecmp(aaa, "type=", 5))
535 mret[0]->type = atoi(&aaa[5]);
536 else if (!strncasecmp(aaa, "msgn=", 5))
537 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
538 else if (!strncasecmp(aaa, "subj=", 5))
539 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
540 else if (!strncasecmp(aaa, "rfca=", 5))
541 safestrncpy(mret[0]->email, &aaa[5], SIZ);
542 else if (!strncasecmp(aaa, "hnod=", 5))
543 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
544 else if (!strncasecmp(aaa, "room=", 5))
545 safestrncpy(mret[0]->room, &aaa[5], SIZ);
546 else if (!strncasecmp(aaa, "node=", 5))
547 safestrncpy(mret[0]->node, &aaa[5], SIZ);
548 else if (!strncasecmp(aaa, "rcpt=", 5))
549 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
550 else if (!strncasecmp(aaa, "wefw=", 5))
551 safestrncpy(mret[0]->references, &aaa[5], SIZ);
552 else if (!strncasecmp(aaa, "time=", 5))
553 mret[0]->time = atol(&aaa[5]);
555 /* Multipart/alternative prefix & suffix strings help
556 * us to determine which part we want to download.
558 else if (!strncasecmp(aaa, "pref=", 5)) {
559 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
560 if (!strcasecmp(multipart_prefix,
561 "multipart/alternative")) {
565 else if (!strncasecmp(aaa, "suff=", 5)) {
566 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
567 if (!strcasecmp(multipart_prefix,
568 "multipart/alternative")) {
573 else if (!strncasecmp(aaa, "part=", 5)) {
574 struct parts *ptr, *chain;
576 ptr = (struct parts *)calloc(1, sizeof (struct parts));
579 /* Fill the buffers for the caller */
580 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
581 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
582 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
583 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
584 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
585 ptr->length = extract_long(&aaa[5], 5);
586 if (!mret[0]->attachments)
587 mret[0]->attachments = ptr;
589 chain = mret[0]->attachments;
595 /* Now handle multipart/alternative */
596 if (multipart_hunting > 0) {
597 if ( (!strcasecmp(ptr->mimetype,
599 || (!strcasecmp(ptr->mimetype,
601 strcpy(mret[0]->mime_chosen,
609 /* Eliminate "text\n" */
610 remove_token(bbb, 0, '\n');
612 /* If doing a MIME thing, pull out the extra headers */
615 if (!strncasecmp(bbb, "Content-type:", 13)) {
616 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
617 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
618 striplt(mret[0]->content_type);
620 /* strip out ";charset=" portion. FIXME do something with
621 * the charset (like... convert it) instead of just throwing
624 if (strstr(mret[0]->content_type, ";") != NULL) {
625 strcpy(strstr(mret[0]->content_type, ";"), "");
629 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
630 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
631 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
632 striplt(mret[0]->mime_chosen);
634 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
635 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
636 strcpy(encoding, &encoding[26]);
639 remove_token(bbb, 0, '\n');
640 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
641 remove_token(bbb, 0, '\n');
648 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
650 int bytes_decoded = 0;
651 ccc = malloc(strlen(bbb) + 32768);
652 if (!strcasecmp(encoding, "base64")) {
653 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
655 else if (!strcasecmp(encoding, "quoted-printable")) {
656 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
658 ccc[bytes_decoded] = 0;
663 /* FIXME: Strip trailing whitespace */
664 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
667 bbb = (char *)realloc(bbb, 1);
677 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
682 if (!cret) return -2;
683 if (!listing) return -2;
684 if (*listing) return -2;
686 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
692 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
696 char *listing = NULL;
699 if (!cret) return -2;
701 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
702 if (ret / 100 == 1) {
705 while (*listing && strlen(listing)) {
706 extract_token(buf, listing, 0, '\n', sizeof buf);
707 remove_token(listing, 0, '\n');
709 case 0: ipc->ServInfo.pid = atoi(buf);
711 case 1: strcpy(ipc->ServInfo.nodename,buf);
713 case 2: strcpy(ipc->ServInfo.humannode,buf);
715 case 3: strcpy(ipc->ServInfo.fqdn,buf);
717 case 4: strcpy(ipc->ServInfo.software,buf);
719 case 5: ipc->ServInfo.rev_level = atoi(buf);
721 case 6: strcpy(ipc->ServInfo.site_location,buf);
723 case 7: strcpy(ipc->ServInfo.sysadm,buf);
725 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
727 case 10: ipc->ServInfo.ok_floors = atoi(buf);
729 case 11: ipc->ServInfo.paging_level = atoi(buf);
731 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
733 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
735 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
737 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
739 case 17: ipc->ServInfo.load_avg = atof(buf);
741 case 18: ipc->ServInfo.worker_avg = atof(buf);
743 case 19: ipc->ServInfo.thread_count = atoi(buf);
745 case 20: ipc->ServInfo.has_sieve = atoi(buf);
747 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
749 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
751 case 24: ipc->ServInfo.guest_logins = atoi(buf);
757 if (listing) free(listing);
763 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
768 if (!cret) return -2;
769 if (!listing) return -2;
770 if (*listing) return -2;
772 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
778 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
780 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
785 if (!cret) return -2;
788 sprintf(aaa, "SLRP %ld", msgnum);
791 sprintf(aaa, "SLRP HIGHEST");
793 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
799 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
804 if (!cret) return -2;
805 if (!username) return -2;
807 aaa = (char *)malloc(strlen(username) + 6);
810 sprintf(aaa, "INVT %s", username);
811 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
818 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
823 if (!cret) return -1;
824 if (!username) return -1;
826 aaa = (char *)malloc(strlen(username) + 6);
828 sprintf(aaa, "KICK %s", username);
829 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
836 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
840 if (!cret) return -2;
841 if (!qret) return -2;
842 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
843 if (!*qret) return -1;
845 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
846 if (ret / 100 == 2) {
847 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
848 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
849 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
850 qret[0]->QRflags = extract_int(cret, 3);
851 qret[0]->QRfloor = extract_int(cret, 4);
852 qret[0]->QRorder = extract_int(cret, 5);
853 qret[0]->QRdefaultview = extract_int(cret, 6);
854 qret[0]->QRflags2 = extract_int(cret, 7);
861 /* set forget to kick all users out of room */
862 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
867 if (!cret) return -2;
868 if (!qret) return -2;
870 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
871 strlen(qret->QRdirname) + 64);
874 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
875 qret->QRname, qret->QRpasswd, qret->QRdirname,
876 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
877 qret->QRdefaultview, qret->QRflags2);
878 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
885 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
887 if (!cret) return -1;
889 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
894 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
899 if (!cret) return -2;
900 if (!username) return -2;
902 aaa = (char *)malloc(strlen(username) + 6);
905 sprintf(aaa, "SETA %s", username);
906 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
913 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
919 if (!cret) return -2;
922 if (mr->references) {
923 for (ptr=mr->references; *ptr != 0; ++ptr) {
924 if (*ptr == '|') *ptr = '!';
928 snprintf(cmd, sizeof cmd,
929 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
930 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
931 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
933 if ((flag == 0) && (subject_required != NULL)) {
934 /* Is the server strongly recommending that the user enter a message subject? */
935 if ((cret[3] != '\0') && (cret[4] != '\0')) {
936 *subject_required = extract_int(&cret[4], 1);
946 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
950 if (!cret) return -2;
951 if (!iret) return -2;
952 if (*iret) return -2;
954 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
959 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
963 if (!cret) return -2;
964 if (!msgnum) return -2;
966 sprintf(aaa, "DELE %ld", msgnum);
967 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
972 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
977 if (!cret) return -2;
978 if (!destroom) return -2;
979 if (!msgnum) return -2;
981 aaa = (char *)malloc(strlen(destroom) + 28);
984 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
985 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
992 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
996 if (!cret) return -2;
998 sprintf(aaa, "KILL %d", for_real);
999 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1004 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
1005 const char *password, int floor, char *cret)
1010 if (!cret) return -2;
1011 if (!roomname) return -2;
1014 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
1015 if (!aaa) return -1;
1016 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
1019 aaa = (char *)malloc(strlen(roomname) + 40);
1020 if (!aaa) return -1;
1021 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1024 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1031 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1033 if (!cret) return -2;
1035 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1040 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1046 if (!cret) return -2;
1047 if (!mret) return -2;
1048 if (*mret) return -2;
1049 if (!message) return -2;
1051 aaa = (char *)malloc(strlen(message) + 6);
1052 if (!aaa) return -1;
1054 sprintf(aaa, "MESG %s", message);
1055 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1062 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1064 if (!cret) return -2;
1066 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1071 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1077 if (!cret) return -2;
1078 if (!rret) return -2;
1079 if (*rret) return -2;
1082 aaa = (char *)malloc(strlen(username) + 6);
1084 aaa = (char *)malloc(12);
1085 if (!aaa) return -1;
1088 sprintf(aaa, "GREG %s", username);
1090 sprintf(aaa, "GREG _SELF_");
1091 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1098 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1103 if (!cret) return -2;
1104 if (!username) return -2;
1105 if (axlevel < AxDeleted || axlevel > AxAideU) return -2;
1107 aaa = (char *)malloc(strlen(username) + 17);
1108 if (!aaa) return -1;
1110 sprintf(aaa, "VALI %s|%d", username, axlevel);
1111 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1118 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1122 if (!cret) return -1;
1123 if (!info) return -1;
1125 sprintf(aaa, "EINF %d", for_real);
1126 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1131 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1137 if (!cret) return -1;
1138 if (!listing) return -1;
1139 if (*listing) return -1;
1140 if (!searchstring) return -1;
1142 cmd = malloc(strlen(searchstring) + 10);
1143 sprintf(cmd, "LIST %s", searchstring);
1145 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1152 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1154 if (!cret) return -1;
1155 if (!info) return -1;
1157 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1163 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1167 if (!cret) return -1;
1168 if (!chek) return -1;
1170 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1171 if (ret / 100 == 2) {
1172 chek->newmail = extract_long(cret, 0);
1173 chek->needregis = extract_int(cret, 1);
1174 chek->needvalid = extract_int(cret, 2);
1181 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1186 if (!cret) return -2;
1187 if (!filename) return -2;
1189 aaa = (char *)malloc(strlen(filename) + 6);
1190 if (!aaa) return -1;
1192 sprintf(aaa, "DELF %s", filename);
1193 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1200 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1205 if (!cret) return -2;
1206 if (!filename) return -2;
1207 if (!destroom) return -2;
1209 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1210 if (!aaa) return -1;
1212 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1213 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1220 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1225 if (!cret) return -1;
1226 if (!listing) return -1;
1227 if (*listing) return -1;
1229 *stamp = CtdlIPCServerTime(ipc, cret);
1231 *stamp = time(NULL);
1232 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1238 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1240 void (*progress_gauge_callback)
1241 (CtdlIPC*, unsigned long, unsigned long),
1250 if (!cret) return -2;
1251 if (!filename) return -2;
1252 if (!buf) return -2;
1253 if (*buf) return -2;
1254 if (ipc->downloading) return -2;
1256 aaa = (char *)malloc(strlen(filename) + 6);
1257 if (!aaa) return -1;
1259 sprintf(aaa, "OPEN %s", filename);
1260 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1262 if (ret / 100 == 2) {
1263 ipc->downloading = 1;
1264 bytes = extract_long(cret, 0);
1265 last_mod = extract_int(cret, 1);
1266 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1268 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1269 progress_gauge_callback, cret);
1271 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1272 progress_gauge_callback, cret);
1275 ret = CtdlIPCEndDownload(ipc, cret);
1277 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1278 filename, mimetype);
1285 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1287 void (*progress_gauge_callback)
1288 (CtdlIPC*, unsigned long, unsigned long),
1298 if (!cret) return -2;
1299 if (!buf) return -2;
1300 if (*buf) return -2;
1301 if (!part) return -2;
1302 if (!msgnum) return -2;
1303 if (ipc->downloading) return -2;
1305 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1306 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1307 if (ret / 100 == 2) {
1308 ipc->downloading = 1;
1309 bytes = extract_long(cret, 0);
1310 last_mod = extract_int(cret, 1);
1311 extract_token(filename, cret, 2, '|', sizeof filename);
1312 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1313 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1314 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1315 ret = CtdlIPCEndDownload(ipc, cret);
1317 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1318 filename, mimetype);
1325 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1326 void (*progress_gauge_callback)
1327 (CtdlIPC*, unsigned long, unsigned long),
1336 if (!cret) return -1;
1337 if (!buf) return -1;
1338 if (*buf) return -1;
1339 if (!filename) return -1;
1340 if (ipc->downloading) return -1;
1342 aaa = (char *)malloc(strlen(filename) + 6);
1343 if (!aaa) return -1;
1345 sprintf(aaa, "OIMG %s", filename);
1346 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1348 if (ret / 100 == 2) {
1349 ipc->downloading = 1;
1350 bytes = extract_long(cret, 0);
1351 last_mod = extract_int(cret, 1);
1352 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1353 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1354 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1355 ret = CtdlIPCEndDownload(ipc, cret);
1357 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1358 filename, mimetype);
1365 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1367 void (*progress_gauge_callback)
1368 (CtdlIPC*, unsigned long, unsigned long),
1374 char MimeTestBuf[64];
1375 const char *MimeType;
1378 if (!cret) return -1;
1379 if (!save_as) return -1;
1380 if (!comment) return -1;
1381 if (!path) return -1;
1382 if (!*path) return -1;
1383 if (ipc->uploading) return -1;
1385 uploadFP = fopen(path, "r");
1386 if (!uploadFP) return -2;
1388 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1393 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1394 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1395 if (!aaa) return -1;
1397 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1398 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1400 if (ret / 100 == 2) {
1402 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1403 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1411 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1412 const char *save_as,
1413 void (*progress_gauge_callback)
1414 (CtdlIPC*, unsigned long, unsigned long),
1420 char MimeTestBuf[64];
1421 const char *MimeType;
1424 if (!cret) return -1;
1425 if (!save_as) return -1;
1426 if (!path && for_real) return -1;
1427 if (!*path && for_real) return -1;
1428 if (ipc->uploading) return -1;
1430 aaa = (char *)malloc(strlen(save_as) + 17);
1431 if (!aaa) return -1;
1433 uploadFP = fopen(path, "r");
1434 if (!uploadFP) return -2;
1436 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1440 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1442 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1443 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1445 if (ret / 100 == 2 && for_real) {
1447 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1448 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1456 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1461 if (!cret) return -2;
1462 if (!username) return -2;
1464 aaa = (char *)malloc(strlen(username) + 6);
1465 if (!aaa) return -1;
1467 sprintf(aaa, "QUSR %s", username);
1468 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1475 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1479 if (!cret) return -2;
1480 if (!listing) return -2;
1481 if (*listing) return -2;
1483 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1488 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1493 if (!cret) return -2;
1494 if (!name) return -2;
1496 sprintf(aaa, "CFLR %s|%d", name, for_real);
1497 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1503 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1507 if (!cret) return -1;
1508 if (floornum < 0) return -1;
1510 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1511 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1516 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1521 if (!cret) return -2;
1522 if (!floorname) return -2;
1523 if (floornum < 0) return -2;
1525 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1526 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1534 * You only need to fill out hostname, the defaults will be used if any of the
1535 * other fields are not set properly.
1537 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1538 int revision, const char *software_name, const char *hostname,
1544 if (developerid < 0 || clientid < 0 || revision < 0 ||
1548 revision = REV_LEVEL - 600;
1549 software_name = "Citadel (libcitadel)";
1551 if (!hostname) return -2;
1553 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1554 if (!aaa) return -1;
1556 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1557 revision, software_name, hostname);
1558 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1565 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1571 if (!cret) return -2;
1572 if (!username) return -2;
1574 aaa = (char *)malloc(strlen(username) + 8);
1575 if (!aaa) return -1;
1578 sprintf(aaa, "SEXP %s|-", username);
1579 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1582 sprintf(aaa, "SEXP %s||", username);
1583 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1591 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1595 if (!cret) return -2;
1596 if (!listing) return -2;
1597 if (*listing) return -2;
1599 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1604 /* mode is 0 = enable, 1 = disable, 2 = status */
1605 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1609 if (!cret) return -2;
1611 sprintf(aaa, "DEXP %d", mode);
1612 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1617 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1619 if (!cret) return -2;
1620 if (!bio) return -2;
1622 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1628 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1634 if (!cret) return -2;
1635 if (!username) return -2;
1636 if (!listing) return -2;
1637 if (*listing) return -2;
1639 aaa = (char *)malloc(strlen(username) + 6);
1640 if (!aaa) return -1;
1642 sprintf(aaa, "RBIO %s", username);
1643 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1650 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1654 if (!cret) return -2;
1655 if (!listing) return -2;
1656 if (*listing) return -2;
1658 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1663 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1667 if (!cret) return -1;
1669 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1670 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1675 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1679 if (!cret) return -1;
1681 sprintf(aaa, "TERM %d", sid);
1682 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1687 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1689 if (!cret) return -1;
1691 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1696 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1700 if (!cret) return -1;
1702 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1703 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1708 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1714 if (!cret) return -2;
1715 if (!text) return -2;
1716 if (!filename) return -2;
1718 aaa = (char *)malloc(strlen(filename) + 6);
1719 if (!aaa) return -1;
1721 sprintf(aaa, "EMSG %s", filename);
1722 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1729 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1734 if (!cret) return -2;
1735 if (!hostname) return -2;
1737 aaa = (char *)malloc(strlen(hostname) + 6);
1738 if (!aaa) return -1;
1740 sprintf(aaa, "HCHG %s", hostname);
1741 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1748 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1753 if (!cret) return -2;
1754 if (!roomname) return -2;
1756 aaa = (char *)malloc(strlen(roomname) + 6);
1757 if (!aaa) return -1;
1759 sprintf(aaa, "RCHG %s", roomname);
1760 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1767 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1772 if (!cret) return -2;
1773 if (!username) return -2;
1775 aaa = (char *)malloc(strlen(username) + 6);
1776 if (!aaa) return -1;
1778 sprintf(aaa, "UCHG %s", username);
1779 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1786 /* This function returns the actual server time reported, or 0 if error */
1787 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1789 register time_t tret;
1792 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1793 if (ret / 100 == 2) {
1794 tret = extract_long(cret, 0);
1803 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1804 struct ctdluser **uret, char *cret)
1809 if (!cret) return -2;
1810 if (!uret) return -2;
1811 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1812 if (!*uret) return -1;
1814 sprintf(aaa, "AGUP %s", who);
1815 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1817 if (ret / 100 == 2) {
1818 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1819 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1820 uret[0]->flags = extract_int(cret, 2);
1821 uret[0]->timescalled = extract_long(cret, 3);
1822 uret[0]->posted = extract_long(cret, 4);
1823 uret[0]->axlevel = extract_int(cret, 5);
1824 uret[0]->usernum = extract_long(cret, 6);
1825 uret[0]->lastcall = extract_long(cret, 7);
1826 uret[0]->USuserpurge = extract_int(cret, 8);
1833 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1838 if (!cret) return -2;
1839 if (!uret) return -2;
1841 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1842 if (!aaa) return -1;
1844 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1845 uret->fullname, uret->password, uret->flags,
1846 uret->timescalled, uret->posted, uret->axlevel,
1847 uret->usernum, uret->lastcall, uret->USuserpurge);
1848 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1855 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1856 /* caller must free the struct ExpirePolicy */
1857 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
1858 struct ExpirePolicy **policy, char *cret)
1860 static char *proto[] = {
1864 strof(mailboxespolicy)
1869 if (!cret) return -2;
1870 if (!policy) return -2;
1871 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1872 if (!*policy) return -1;
1873 if (which < 0 || which > 3) return -2;
1875 sprintf(cmd, "GPEX %s", proto[which]);
1876 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1877 if (ret / 100 == 2) {
1878 policy[0]->expire_mode = extract_int(cret, 0);
1879 policy[0]->expire_value = extract_int(cret, 1);
1886 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1887 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1888 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1889 struct ExpirePolicy *policy, char *cret)
1892 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1894 if (!cret) return -2;
1895 if (which < 0 || which > 3) return -2;
1896 if (!policy) return -2;
1897 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1898 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1900 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1901 policy->expire_mode, policy->expire_value);
1902 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1907 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1911 if (!cret) return -2;
1912 if (!listing) return -2;
1913 if (*listing) return -2;
1915 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1916 listing, &bytes, cret);
1921 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1923 if (!cret) return -2;
1924 if (!listing) return -2;
1926 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1932 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1933 char **listing, char *cret)
1939 if (!cret) return -2;
1940 if (!mimetype) return -2;
1941 if (!listing) return -2;
1942 if (*listing) return -2;
1944 aaa = malloc(strlen(mimetype) + 13);
1945 if (!aaa) return -1;
1946 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1947 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1948 listing, &bytes, cret);
1955 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1956 const char *listing, char *cret)
1961 if (!cret) return -2;
1962 if (!mimetype) return -2;
1963 if (!listing) return -2;
1965 aaa = malloc(strlen(mimetype) + 13);
1966 if (!aaa) return -1;
1967 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1968 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1976 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1980 if (!cret) return -2;
1981 if (!listing) return -2;
1982 if (*listing) return -2;
1984 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1985 listing, &bytes, cret);
1990 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1992 if (!cret) return -2;
1993 if (!listing) return -2;
1995 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
2001 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2005 if (!cret) return -2;
2006 if (session < 0) return -2;
2008 sprintf(aaa, "REQT %d", session);
2009 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2014 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2018 if (!cret) return -2;
2019 if (msgnum < 0) return -2;
2021 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2022 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2027 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2036 /* New SSL object */
2037 temp_ssl = SSL_new(ssl_ctx);
2039 error_printf("SSL_new failed: %s\n",
2040 ERR_reason_error_string(ERR_get_error()));
2043 /* Pointless flag waving */
2044 #if SSLEAY_VERSION_NUMBER >= 0x0922
2045 SSL_set_session_id_context(temp_ssl, (const unsigned char*) "Citadel SID", 14);
2048 if (!access(EGD_POOL, F_OK))
2051 if (!RAND_status()) {
2052 error_printf("PRNG not properly seeded\n");
2056 /* Associate network connection with SSL object */
2057 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2058 error_printf("SSL_set_fd failed: %s\n",
2059 ERR_reason_error_string(ERR_get_error()));
2063 if (status_hook != NULL)
2064 status_hook("Requesting encryption...\r");
2066 /* Ready to start SSL/TLS */
2068 CtdlIPC_putline(ipc, "STLS");
2069 CtdlIPC_getline(ipc, buf);
2070 if (buf[0] != '2') {
2071 error_printf("Server can't start TLS: %s\n", buf);
2075 r = CtdlIPCGenericCommand(ipc,
2076 "STLS", NULL, 0, NULL, NULL, cret);
2078 error_printf("Server can't start TLS: %s\n", buf);
2083 /* Do SSL/TLS handshake */
2084 if ((a = SSL_connect(temp_ssl)) < 1) {
2085 error_printf("SSL_connect failed: %s\n",
2086 ERR_reason_error_string(ERR_get_error()));
2090 ipc->ssl = temp_ssl;
2092 if (BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE))
2096 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2097 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2098 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2099 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2105 #endif /* HAVE_OPENSSL */
2110 static void endtls(SSL *ssl)
2121 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2126 if (!address) return -2;
2127 if (!cret) return -2;
2129 aaa = (char *)malloc(strlen(address) + 6);
2130 if (!aaa) return -1;
2132 sprintf(aaa, "QDIR %s", address);
2133 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2140 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2144 if (!cret) return -2;
2145 sprintf(aaa, "IPGM %d", secret);
2146 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2168 /* ************************************************************************** */
2169 /* Stuff below this line is not for public consumption */
2170 /* ************************************************************************** */
2173 /* Read a listing from the server up to 000. Append to dest if it exists */
2174 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2183 length = strlen(ret);
2188 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2189 linelength = strlen(aaa);
2190 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2192 strcpy(&ret[length], aaa);
2193 length += linelength;
2194 strcpy(&ret[length++], "\n");
2202 /* Send a listing to the server; generate the ending 000. */
2203 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2207 text = (char *)malloc(strlen(listing) + 6);
2209 strcpy(text, listing);
2210 while (text[strlen(text) - 1] == '\n')
2211 text[strlen(text) - 1] = '\0';
2212 strcat(text, "\n000");
2213 CtdlIPC_putline(ipc, text);
2217 /* Malloc failed but we are committed to send */
2218 /* This may result in extra blanks at the bottom */
2219 CtdlIPC_putline(ipc, text);
2220 CtdlIPC_putline(ipc, "000");
2226 /* Partial read of file from server */
2227 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2229 register size_t len = 0;
2233 if (!cret) return 0;
2234 if (bytes < 1) return 0;
2237 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2238 CtdlIPC_putline(ipc, aaa);
2239 CtdlIPC_getline(ipc, aaa);
2241 strcpy(cret, &aaa[4]);
2243 len = extract_long(&aaa[4], 0);
2244 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2246 /* I know what I'm doing */
2247 serv_read(ipc, ((char *)(*buf) + offset), len);
2249 /* We have to read regardless */
2250 serv_read(ipc, aaa, len);
2254 CtdlIPC_unlock(ipc);
2260 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2264 if (!cret) return -2;
2265 if (!ipc->downloading) return -2;
2267 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2269 ipc->downloading = 0;
2275 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2279 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2280 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2287 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2288 void (*progress_gauge_callback)
2289 (CtdlIPC*, unsigned long, unsigned long),
2292 register size_t len;
2294 if (!cret) return -1;
2295 if (!buf) return -1;
2296 if (*buf) return -1;
2297 if (!ipc->downloading) return -1;
2300 if (progress_gauge_callback)
2301 progress_gauge_callback(ipc, len, bytes);
2302 while (len < bytes) {
2303 register size_t block;
2305 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2311 if (progress_gauge_callback)
2312 progress_gauge_callback(ipc, len, bytes);
2317 /* READ - pipelined */
2318 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2320 void (*progress_gauge_callback)
2321 (CtdlIPC*, unsigned long, unsigned long),
2324 register size_t len;
2325 register int calls; /* How many calls in the pipeline */
2326 register int i; /* iterator */
2329 if (!cret) return -1;
2330 if (!buf) return -1;
2331 if (*buf) return -1;
2332 if (!ipc->downloading) return -1;
2334 *buf = (void *)realloc(*buf, bytes - resume);
2335 if (!*buf) return -1;
2339 if (progress_gauge_callback)
2340 progress_gauge_callback(ipc, len, bytes);
2342 /* How many calls will be in the pipeline? */
2343 calls = (bytes - resume) / 4096;
2344 if ((bytes - resume) % 4096) calls++;
2346 /* Send all requests at once */
2347 for (i = 0; i < calls; i++) {
2348 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2349 CtdlIPC_putline(ipc, aaa);
2352 /* Receive all responses at once */
2353 for (i = 0; i < calls; i++) {
2354 CtdlIPC_getline(ipc, aaa);
2356 strcpy(cret, &aaa[4]);
2358 len = extract_long(&aaa[4], 0);
2359 /* I know what I'm doing */
2360 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2362 if (progress_gauge_callback)
2363 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2365 CtdlIPC_unlock(ipc);
2371 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2376 if (!cret) return -1;
2377 if (!ipc->uploading) return -1;
2379 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2380 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2387 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2388 void (*progress_gauge_callback)
2389 (CtdlIPC*, unsigned long, unsigned long),
2392 register int ret = -1;
2393 register size_t offset = 0;
2397 FILE *fd = uploadFP;
2400 if (!cret) return -1;
2402 fseek(fd, 0L, SEEK_END);
2406 if (progress_gauge_callback)
2407 progress_gauge_callback(ipc, 0, bytes);
2409 while (offset < bytes) {
2410 register size_t to_write;
2412 /* Read some data in */
2413 to_write = fread(buf, 1, 4096, fd);
2415 if (feof(fd) || ferror(fd)) break;
2417 sprintf(aaa, "WRIT %d", (int)to_write);
2418 CtdlIPC_putline(ipc, aaa);
2419 CtdlIPC_getline(ipc, aaa);
2420 strcpy(cret, &aaa[4]);
2422 if (aaa[0] == '7') {
2423 to_write = extract_long(&aaa[4], 0);
2425 serv_write(ipc, buf, to_write);
2427 if (progress_gauge_callback)
2428 progress_gauge_callback(ipc, offset, bytes);
2429 /* Detect short reads and back up if needed */
2430 /* offset will never be negative anyway */
2431 fseek(fd, (signed)offset, SEEK_SET);
2436 if (progress_gauge_callback)
2437 progress_gauge_callback(ipc, 1, 1);
2440 return (!ferr ? ret : -2);
2445 * Generic command method. This method should handle any server command
2446 * except for CHAT. It takes the following arguments:
2448 * ipc The server to speak with
2449 * command Preformatted command to send to server
2450 * to_send A text or binary file to send to server
2451 * (only sent if server requests it)
2452 * bytes_to_send The number of bytes in to_send (required if
2453 * sending binary, optional if sending listing)
2454 * to_receive Pointer to a NULL pointer, if the server
2455 * sends text or binary we will allocate memory
2456 * for the file and stuff it here
2457 * bytes_to_receive If a file is received, we will store its
2459 * proto_response The protocol response. Caller must provide
2460 * this buffer and ensure that it is at least
2461 * 128 bytes in length.
2463 * This function returns a number equal to the protocol response number,
2464 * -1 if an internal error occurred, -2 if caller provided bad values,
2465 * or 0 - the protocol response number if bad values were found during
2466 * the protocol exchange.
2467 * It stores the protocol response string (minus the number) in
2468 * protocol_response as described above. Some commands send additional
2469 * data in this string.
2471 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2472 const char *command, const char *to_send,
2473 size_t bytes_to_send, char **to_receive,
2474 size_t *bytes_to_receive, char *proto_response)
2479 if (!command) return -2;
2480 if (!proto_response) return -2;
2483 CtdlIPC_putline(ipc, command);
2485 CtdlIPC_getline(ipc, proto_response);
2486 if (proto_response[3] == '*')
2488 ret = atoi(proto_response);
2489 strcpy(proto_response, &proto_response[4]);
2490 switch (ret / 100) {
2491 default: /* Unknown, punt */
2493 case 3: /* MORE_DATA */
2495 /* Don't need to do anything */
2497 case 1: /* LISTING_FOLLOWS */
2498 if (to_receive && !*to_receive && bytes_to_receive) {
2499 *to_receive = CtdlIPCReadListing(ipc, NULL);
2500 } else { /* Drain */
2501 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2505 case 4: /* SEND_LISTING */
2507 CtdlIPCSendListing(ipc, to_send);
2509 /* No listing given, fake it */
2510 CtdlIPC_putline(ipc, "000");
2514 case 6: /* BINARY_FOLLOWS */
2515 if (to_receive && !*to_receive && bytes_to_receive) {
2517 extract_long(proto_response, 0);
2518 *to_receive = (char *)
2519 malloc((size_t)*bytes_to_receive);
2523 serv_read(ipc, *to_receive,
2530 drain = extract_long(proto_response, 0);
2531 while (drain > SIZ) {
2532 serv_read(ipc, buf, SIZ);
2535 serv_read(ipc, buf, drain);
2539 case 7: /* SEND_BINARY */
2540 if (to_send && bytes_to_send) {
2541 serv_write(ipc, to_send, bytes_to_send);
2542 } else if (bytes_to_send) {
2543 /* Fake it, send nulls */
2546 fake = bytes_to_send;
2547 memset(buf, '\0', SIZ);
2548 while (fake > SIZ) {
2549 serv_write(ipc, buf, SIZ);
2552 serv_write(ipc, buf, fake);
2554 } /* else who knows? DANGER WILL ROBINSON */
2556 case 8: /* START_CHAT_MODE */
2557 if (!strncasecmp(command, "CHAT", 4)) {
2558 /* Don't call chatmode with generic! */
2559 CtdlIPC_putline(ipc, "/quit");
2562 /* In this mode we send then receive listing */
2564 CtdlIPCSendListing(ipc, to_send);
2566 /* No listing given, fake it */
2567 CtdlIPC_putline(ipc, "000");
2570 if (to_receive && !*to_receive
2571 && bytes_to_receive) {
2572 *to_receive = CtdlIPCReadListing(ipc, NULL);
2573 } else { /* Drain */
2574 while (CtdlIPC_getline(ipc, buf),
2575 strcmp(buf, "000")) ;
2580 case 9: /* ASYNC_MSG */
2581 /* CtdlIPCDoAsync(ret, proto_response); */
2582 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2588 CtdlIPC_unlock(ipc);
2594 * Connect to a Citadel on a remote host using a TCP/IP socket
2596 static int tcp_connectsock(char *host, char *service)
2598 struct in6_addr serveraddr;
2599 struct addrinfo hints;
2600 struct addrinfo *res = NULL;
2601 struct addrinfo *ai = NULL;
2605 if ((host == NULL) || IsEmptyStr(host)) {
2606 service = DEFAULT_HOST ;
2608 if ((service == NULL) || IsEmptyStr(service)) {
2609 service = DEFAULT_PORT ;
2612 memset(&hints, 0x00, sizeof(hints));
2613 hints.ai_flags = AI_NUMERICSERV;
2614 hints.ai_family = AF_UNSPEC;
2615 hints.ai_socktype = SOCK_STREAM;
2618 * Handle numeric IPv4 and IPv6 addresses
2620 rc = inet_pton(AF_INET, host, &serveraddr);
2621 if (rc == 1) { /* dotted quad */
2622 hints.ai_family = AF_INET;
2623 hints.ai_flags |= AI_NUMERICHOST;
2626 rc = inet_pton(AF_INET6, host, &serveraddr);
2627 if (rc == 1) { /* IPv6 address */
2628 hints.ai_family = AF_INET6;
2629 hints.ai_flags |= AI_NUMERICHOST;
2633 /* Begin the connection process */
2635 rc = getaddrinfo(host, service, &hints, &res);
2641 * Try all available addresses until we connect to one or until we run out.
2643 for (ai = res; ai != NULL; ai = ai->ai_next) {
2644 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2645 if (sock < 0) return(-1);
2647 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2649 return(sock); /* Connected! */
2652 close(sock); /* Failed. Close the socket to avoid fd leak! */
2664 * Connect to a Citadel on the local host using a unix domain socket
2666 static int uds_connectsock(int *isLocal, char *sockpath)
2668 struct sockaddr_un addr;
2671 memset(&addr, 0, sizeof(addr));
2672 addr.sun_family = AF_UNIX;
2673 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2675 s = socket(AF_UNIX, SOCK_STREAM, 0);
2680 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2691 * input binary data from socket
2693 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2695 unsigned int len, rlen;
2697 #if defined(HAVE_OPENSSL)
2699 serv_read_ssl(ipc, buf, bytes);
2704 while (len < bytes) {
2705 rlen = read(ipc->sock, &buf[len], bytes - len);
2707 connection_died(ipc, 0);
2716 * send binary to server
2718 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2720 unsigned int bytes_written = 0;
2723 #if defined(HAVE_OPENSSL)
2725 serv_write_ssl(ipc, buf, nbytes);
2729 while (bytes_written < nbytes) {
2730 retval = write(ipc->sock, &buf[bytes_written],
2731 nbytes - bytes_written);
2733 connection_died(ipc, 0);
2736 bytes_written += retval;
2743 * input binary data from encrypted connection
2745 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2751 while (len < bytes) {
2752 if (SSL_want_read(ipc->ssl)) {
2753 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2754 error_printf("SSL_write in serv_read:\n");
2755 ERR_print_errors_fp(stderr);
2758 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2762 errval = SSL_get_error(ipc->ssl, rlen);
2763 if (errval == SSL_ERROR_WANT_READ ||
2764 errval == SSL_ERROR_WANT_WRITE) {
2769 Not sure why we'd want to handle these error codes any differently,
2770 but this definitely isn't the way to handle them. Someone must have
2771 naively assumed that we could fall back to unencrypted communications,
2772 but all it does is just recursively blow the stack.
2773 if (errval == SSL_ERROR_ZERO_RETURN ||
2774 errval == SSL_ERROR_SSL) {
2775 serv_read(ipc, &buf[len], bytes - len);
2779 error_printf("SSL_read in serv_read: %s\n",
2780 ERR_reason_error_string(ERR_peek_error()));
2781 connection_died(ipc, 1);
2790 * send binary to server encrypted
2792 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2794 unsigned int bytes_written = 0;
2798 while (bytes_written < nbytes) {
2799 if (SSL_want_write(ipc->ssl)) {
2800 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2801 error_printf("SSL_read in serv_write:\n");
2802 ERR_print_errors_fp(stderr);
2805 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2806 nbytes - bytes_written);
2810 errval = SSL_get_error(ipc->ssl, retval);
2811 if (errval == SSL_ERROR_WANT_READ ||
2812 errval == SSL_ERROR_WANT_WRITE) {
2816 if (errval == SSL_ERROR_ZERO_RETURN ||
2817 errval == SSL_ERROR_SSL) {
2818 serv_write(ipc, &buf[bytes_written],
2819 nbytes - bytes_written);
2822 error_printf("SSL_write in serv_write: %s\n",
2823 ERR_reason_error_string(ERR_peek_error()));
2824 connection_died(ipc, 1);
2827 bytes_written += retval;
2832 #ifdef THREADED_CLIENT
2833 static void ssl_lock(int mode, int n, const char *file, int line)
2835 if (mode & CRYPTO_LOCK)
2836 pthread_mutex_lock(Critters[n]);
2838 pthread_mutex_unlock(Critters[n]);
2840 #endif /* THREADED_CLIENT */
2843 static void CtdlIPC_init_OpenSSL(void)
2846 const SSL_METHOD *ssl_method;
2849 /* already done init */
2858 SSL_load_error_strings();
2859 SSLeay_add_ssl_algorithms();
2861 /* Set up the SSL context in which we will oeprate */
2862 ssl_method = SSLv23_client_method();
2863 ssl_ctx = SSL_CTX_new(ssl_method);
2865 error_printf("SSL_CTX_new failed: %s\n",
2866 ERR_reason_error_string(ERR_get_error()));
2869 /* Any reasonable cipher we can get */
2870 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2871 error_printf("No ciphers available for encryption\n");
2874 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2876 /* Load DH parameters into the context */
2879 error_printf("Can't allocate a DH object: %s\n",
2880 ERR_reason_error_string(ERR_get_error()));
2883 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2884 error_printf("Can't assign DH_P: %s\n",
2885 ERR_reason_error_string(ERR_get_error()));
2889 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2890 error_printf("Can't assign DH_G: %s\n",
2891 ERR_reason_error_string(ERR_get_error()));
2896 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2899 #ifdef THREADED_CLIENT
2900 /* OpenSSL requires callbacks for threaded clients */
2901 CRYPTO_set_locking_callback(ssl_lock);
2902 CRYPTO_set_id_callback(id_callback);
2904 /* OpenSSL requires us to do semaphores for threaded clients */
2905 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2907 perror("malloc failed");
2910 for (a = 0; a < CRYPTO_num_locks(); a++) {
2911 Critters[a] = malloc(sizeof (pthread_mutex_t));
2913 perror("malloc failed");
2916 pthread_mutex_init(Critters[a], NULL);
2919 #endif /* THREADED_CLIENT */
2924 #ifdef THREADED_CLIENT
2925 static unsigned long id_callback(void) {
2926 return (unsigned long)pthread_self();
2928 #endif /* THREADED_CLIENT */
2929 #endif /* HAVE_OPENSSL */
2933 ReadNetworkChunk(CtdlIPC* ipc)
2950 FD_SET(ipc->sock, &read_fd);
2951 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2953 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2957 *(ipc->BufPtr) = '\0';
2958 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2959 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2961 ipc->BufPtr[n]='\0';
2969 if (!(errno == EINTR || errno == EAGAIN))
2970 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2976 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2978 ipc->BufPtr[n]='\0';
2983 connection_died(ipc, 0);
2991 * input string from socket - implemented in terms of serv_read()
2995 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2998 char *aptr, *bptr, *aeptr, *beptr;
3000 // error_printf("---\n");
3003 #if defined(HAVE_OPENSSL)
3006 /* Read one character at a time. */
3008 serv_read(ipc, &buf[i], 1);
3009 if (buf[i] == '\n' || i == (SIZ-1))
3013 /* If we got a long line, discard characters until the newline. */
3015 while (buf[i] != '\n')
3016 serv_read(ipc, &buf[i], 1);
3018 /* Strip the trailing newline (and carriage return, if present) */
3019 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3020 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3025 if (ipc->Buf == NULL)
3028 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3030 ipc->BufPtr = ipc->Buf;
3034 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3035 if (ipc->BufUsed == 0)
3036 ReadNetworkChunk(ipc);
3038 //// if (ipc->BufUsed != 0) while (1)
3044 aeptr = ipc->Buf + ipc->BufSize;
3045 while ((aptr < aeptr) &&
3049 *(bptr++) = *(aptr++);
3050 if ((*aptr == '\n') && (aptr < aeptr))
3052 /* Terminate it right, remove the line breaks */
3053 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3055 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3058 // 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);
3059 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3062 /* is there more in the buffer we need to read later? */
3063 if (ipc->Buf + ipc->BufUsed > aptr)
3070 ipc->BufPtr = ipc->Buf;
3072 // error_printf("----bla6\n");
3075 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3076 else if ((ipc->BufPtr != ipc->Buf) &&
3077 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3079 size_t NewBufSize = ipc->BufSize * 2;
3080 int delta = (ipc->BufPtr - ipc->Buf);
3083 /* if the line would end after our buffer, we should use a bigger buffer. */
3084 NewBuf = (char *)malloc (NewBufSize + 10);
3085 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3087 ipc->Buf = ipc->BufPtr = NewBuf;
3088 ipc->BufUsed -= delta;
3089 ipc->BufSize = NewBufSize;
3091 if (ReadNetworkChunk(ipc) <0)
3093 // error_printf("----bla\n");
3097 /// error_printf("----bl45761%s\nipc->BufUsed");
3099 // error_printf("----bla1\n");
3102 #else /* CHUNKED_READ */
3104 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3108 /* Read one character at a time. */
3110 serv_read(ipc, &buf[i], 1);
3111 if (buf[i] == '\n' || i == (SIZ-1))
3115 /* If we got a long line, discard characters until the newline. */
3117 while (buf[i] != '\n')
3118 serv_read(ipc, &buf[i], 1);
3120 /* Strip the trailing newline (and carriage return, if present) */
3121 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3122 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3126 #endif /* CHUNKED_READ */
3129 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3131 CtdlIPC_getline(ipc, buf);
3135 * send line to server - implemented in terms of serv_write()
3137 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3143 cmd = malloc(len + 2);
3145 /* This requires no extra memory */
3146 serv_write(ipc, buf, len);
3147 serv_write(ipc, "\n", 1);
3149 /* This is network-optimized */
3150 strncpy(cmd, buf, len);
3151 strcpy(cmd + len, "\n");
3152 serv_write(ipc, cmd, len + 1);
3156 ipc->last_command_sent = time(NULL);
3159 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3161 CtdlIPC_putline(ipc, buf);
3168 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3176 ipc = ialloc(CtdlIPC);
3180 #if defined(HAVE_OPENSSL)
3182 CtdlIPC_init_OpenSSL();
3184 #if defined(HAVE_PTHREAD_H)
3185 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3187 ipc->sock = -1; /* Not connected */
3188 ipc->isLocal = 0; /* Not local, of course! */
3189 ipc->downloading = 0;
3191 ipc->last_command_sent = 0L;
3192 ipc->network_status_cb = NULL;
3197 strcpy(cithost, DEFAULT_HOST); /* default host */
3198 strcpy(citport, DEFAULT_PORT); /* default port */
3200 /* Allow caller to supply our values (Windows) */
3201 if (hostbuf && strlen(hostbuf) > 0)
3202 strcpy(cithost, hostbuf);
3203 if (portbuf && strlen(portbuf) > 0)
3204 strcpy(citport, portbuf);
3206 /* Read host/port from command line if present */
3207 for (a = 0; a < argc; ++a) {
3210 } else if (a == 1) {
3211 strcpy(cithost, argv[a]);
3212 } else if (a == 2) {
3213 strcpy(citport, argv[a]);
3215 error_printf("%s: usage: ",argv[0]);
3216 error_printf("%s [host] [port] ",argv[0]);
3223 if ((!strcmp(cithost, "localhost"))
3224 || (!strcmp(cithost, "127.0.0.1"))) {
3228 /* If we're using a unix domain socket we can do a bunch of stuff */
3229 if (!strcmp(cithost, UDS)) {
3230 if (!strcasecmp(citport, DEFAULT_PORT)) {
3231 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3234 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3236 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3237 if (ipc->sock == -1) {
3241 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3242 if (portbuf != NULL) strcpy(portbuf, sockpath);
3243 strcpy(ipc->ip_hostname, "");
3244 strcpy(ipc->ip_address, "");
3248 ipc->sock = tcp_connectsock(cithost, citport);
3249 if (ipc->sock == -1) {
3255 /* Learn the actual network identity of the host to which we are connected */
3257 struct sockaddr_in6 clientaddr;
3258 unsigned int addrlen = sizeof(clientaddr);
3260 ipc->ip_hostname[0] = 0;
3261 ipc->ip_address[0] = 0;
3263 getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3264 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3265 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3267 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3268 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3271 /* stuff other things elsewhere */
3273 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3274 if (portbuf != NULL) strcpy(portbuf, citport);
3280 * Disconnect and delete the IPC class (destructor)
3282 void CtdlIPC_delete(CtdlIPC* ipc)
3286 SSL_shutdown(ipc->ssl);
3291 if (ipc->sock > -1) {
3292 shutdown(ipc->sock, 2); /* Close it up */
3295 if (ipc->Buf != NULL)
3304 * Disconnect and delete the IPC class (destructor)
3305 * Also NULLs out the pointer
3307 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3309 CtdlIPC_delete(*pipc);
3315 * return the file descriptor of the server socket so we can select() on it.
3317 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3320 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3327 * return one character
3329 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3332 char CtdlIPC_get(CtdlIPC* ipc)
3337 serv_read(ipc, buf, 1);