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);
2151 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2155 if (!cret) return -2;
2156 if (!mret) return -2;
2157 if (*mret) return -2;
2159 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2180 /* ************************************************************************** */
2181 /* Stuff below this line is not for public consumption */
2182 /* ************************************************************************** */
2185 /* Read a listing from the server up to 000. Append to dest if it exists */
2186 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2195 length = strlen(ret);
2200 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2201 linelength = strlen(aaa);
2202 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2204 strcpy(&ret[length], aaa);
2205 length += linelength;
2206 strcpy(&ret[length++], "\n");
2214 /* Send a listing to the server; generate the ending 000. */
2215 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2219 text = (char *)malloc(strlen(listing) + 6);
2221 strcpy(text, listing);
2222 while (text[strlen(text) - 1] == '\n')
2223 text[strlen(text) - 1] = '\0';
2224 strcat(text, "\n000");
2225 CtdlIPC_putline(ipc, text);
2229 /* Malloc failed but we are committed to send */
2230 /* This may result in extra blanks at the bottom */
2231 CtdlIPC_putline(ipc, text);
2232 CtdlIPC_putline(ipc, "000");
2238 /* Partial read of file from server */
2239 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2241 register size_t len = 0;
2245 if (!cret) return 0;
2246 if (bytes < 1) return 0;
2249 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2250 CtdlIPC_putline(ipc, aaa);
2251 CtdlIPC_getline(ipc, aaa);
2253 strcpy(cret, &aaa[4]);
2255 len = extract_long(&aaa[4], 0);
2256 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2258 /* I know what I'm doing */
2259 serv_read(ipc, ((char *)(*buf) + offset), len);
2261 /* We have to read regardless */
2262 serv_read(ipc, aaa, len);
2266 CtdlIPC_unlock(ipc);
2272 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2276 if (!cret) return -2;
2277 if (!ipc->downloading) return -2;
2279 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2281 ipc->downloading = 0;
2287 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2291 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2292 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2299 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2300 void (*progress_gauge_callback)
2301 (CtdlIPC*, unsigned long, unsigned long),
2304 register size_t len;
2306 if (!cret) return -1;
2307 if (!buf) return -1;
2308 if (*buf) return -1;
2309 if (!ipc->downloading) return -1;
2312 if (progress_gauge_callback)
2313 progress_gauge_callback(ipc, len, bytes);
2314 while (len < bytes) {
2315 register size_t block;
2317 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2323 if (progress_gauge_callback)
2324 progress_gauge_callback(ipc, len, bytes);
2329 /* READ - pipelined */
2330 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2332 void (*progress_gauge_callback)
2333 (CtdlIPC*, unsigned long, unsigned long),
2336 register size_t len;
2337 register int calls; /* How many calls in the pipeline */
2338 register int i; /* iterator */
2341 if (!cret) return -1;
2342 if (!buf) return -1;
2343 if (*buf) return -1;
2344 if (!ipc->downloading) return -1;
2346 *buf = (void *)realloc(*buf, bytes - resume);
2347 if (!*buf) return -1;
2351 if (progress_gauge_callback)
2352 progress_gauge_callback(ipc, len, bytes);
2354 /* How many calls will be in the pipeline? */
2355 calls = (bytes - resume) / 4096;
2356 if ((bytes - resume) % 4096) calls++;
2358 /* Send all requests at once */
2359 for (i = 0; i < calls; i++) {
2360 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2361 CtdlIPC_putline(ipc, aaa);
2364 /* Receive all responses at once */
2365 for (i = 0; i < calls; i++) {
2366 CtdlIPC_getline(ipc, aaa);
2368 strcpy(cret, &aaa[4]);
2370 len = extract_long(&aaa[4], 0);
2371 /* I know what I'm doing */
2372 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2374 if (progress_gauge_callback)
2375 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2377 CtdlIPC_unlock(ipc);
2383 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2388 if (!cret) return -1;
2389 if (!ipc->uploading) return -1;
2391 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2392 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2399 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2400 void (*progress_gauge_callback)
2401 (CtdlIPC*, unsigned long, unsigned long),
2404 register int ret = -1;
2405 register size_t offset = 0;
2409 FILE *fd = uploadFP;
2412 if (!cret) return -1;
2414 fseek(fd, 0L, SEEK_END);
2418 if (progress_gauge_callback)
2419 progress_gauge_callback(ipc, 0, bytes);
2421 while (offset < bytes) {
2422 register size_t to_write;
2424 /* Read some data in */
2425 to_write = fread(buf, 1, 4096, fd);
2427 if (feof(fd) || ferror(fd)) break;
2429 sprintf(aaa, "WRIT %d", (int)to_write);
2430 CtdlIPC_putline(ipc, aaa);
2431 CtdlIPC_getline(ipc, aaa);
2432 strcpy(cret, &aaa[4]);
2434 if (aaa[0] == '7') {
2435 to_write = extract_long(&aaa[4], 0);
2437 serv_write(ipc, buf, to_write);
2439 if (progress_gauge_callback)
2440 progress_gauge_callback(ipc, offset, bytes);
2441 /* Detect short reads and back up if needed */
2442 /* offset will never be negative anyway */
2443 fseek(fd, (signed)offset, SEEK_SET);
2448 if (progress_gauge_callback)
2449 progress_gauge_callback(ipc, 1, 1);
2452 return (!ferr ? ret : -2);
2457 * Generic command method. This method should handle any server command
2458 * except for CHAT. It takes the following arguments:
2460 * ipc The server to speak with
2461 * command Preformatted command to send to server
2462 * to_send A text or binary file to send to server
2463 * (only sent if server requests it)
2464 * bytes_to_send The number of bytes in to_send (required if
2465 * sending binary, optional if sending listing)
2466 * to_receive Pointer to a NULL pointer, if the server
2467 * sends text or binary we will allocate memory
2468 * for the file and stuff it here
2469 * bytes_to_receive If a file is received, we will store its
2471 * proto_response The protocol response. Caller must provide
2472 * this buffer and ensure that it is at least
2473 * 128 bytes in length.
2475 * This function returns a number equal to the protocol response number,
2476 * -1 if an internal error occurred, -2 if caller provided bad values,
2477 * or 0 - the protocol response number if bad values were found during
2478 * the protocol exchange.
2479 * It stores the protocol response string (minus the number) in
2480 * protocol_response as described above. Some commands send additional
2481 * data in this string.
2483 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2484 const char *command, const char *to_send,
2485 size_t bytes_to_send, char **to_receive,
2486 size_t *bytes_to_receive, char *proto_response)
2491 if (!command) return -2;
2492 if (!proto_response) return -2;
2495 CtdlIPC_putline(ipc, command);
2497 CtdlIPC_getline(ipc, proto_response);
2498 if (proto_response[3] == '*')
2500 ret = atoi(proto_response);
2501 strcpy(proto_response, &proto_response[4]);
2502 switch (ret / 100) {
2503 default: /* Unknown, punt */
2505 case 3: /* MORE_DATA */
2507 /* Don't need to do anything */
2509 case 1: /* LISTING_FOLLOWS */
2510 if (to_receive && !*to_receive && bytes_to_receive) {
2511 *to_receive = CtdlIPCReadListing(ipc, NULL);
2512 } else { /* Drain */
2513 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2517 case 4: /* SEND_LISTING */
2519 CtdlIPCSendListing(ipc, to_send);
2521 /* No listing given, fake it */
2522 CtdlIPC_putline(ipc, "000");
2526 case 6: /* BINARY_FOLLOWS */
2527 if (to_receive && !*to_receive && bytes_to_receive) {
2529 extract_long(proto_response, 0);
2530 *to_receive = (char *)
2531 malloc((size_t)*bytes_to_receive);
2535 serv_read(ipc, *to_receive,
2542 drain = extract_long(proto_response, 0);
2543 while (drain > SIZ) {
2544 serv_read(ipc, buf, SIZ);
2547 serv_read(ipc, buf, drain);
2551 case 7: /* SEND_BINARY */
2552 if (to_send && bytes_to_send) {
2553 serv_write(ipc, to_send, bytes_to_send);
2554 } else if (bytes_to_send) {
2555 /* Fake it, send nulls */
2558 fake = bytes_to_send;
2559 memset(buf, '\0', SIZ);
2560 while (fake > SIZ) {
2561 serv_write(ipc, buf, SIZ);
2564 serv_write(ipc, buf, fake);
2566 } /* else who knows? DANGER WILL ROBINSON */
2568 case 8: /* START_CHAT_MODE */
2569 if (!strncasecmp(command, "CHAT", 4)) {
2570 /* Don't call chatmode with generic! */
2571 CtdlIPC_putline(ipc, "/quit");
2574 /* In this mode we send then receive listing */
2576 CtdlIPCSendListing(ipc, to_send);
2578 /* No listing given, fake it */
2579 CtdlIPC_putline(ipc, "000");
2582 if (to_receive && !*to_receive
2583 && bytes_to_receive) {
2584 *to_receive = CtdlIPCReadListing(ipc, NULL);
2585 } else { /* Drain */
2586 while (CtdlIPC_getline(ipc, buf),
2587 strcmp(buf, "000")) ;
2592 case 9: /* ASYNC_MSG */
2593 /* CtdlIPCDoAsync(ret, proto_response); */
2594 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2600 CtdlIPC_unlock(ipc);
2606 * Connect to a Citadel on a remote host using a TCP/IP socket
2608 static int tcp_connectsock(char *host, char *service)
2610 struct in6_addr serveraddr;
2611 struct addrinfo hints;
2612 struct addrinfo *res = NULL;
2613 struct addrinfo *ai = NULL;
2617 if ((host == NULL) || IsEmptyStr(host)) {
2618 service = DEFAULT_HOST ;
2620 if ((service == NULL) || IsEmptyStr(service)) {
2621 service = DEFAULT_PORT ;
2624 memset(&hints, 0x00, sizeof(hints));
2625 hints.ai_flags = AI_NUMERICSERV;
2626 hints.ai_family = AF_UNSPEC;
2627 hints.ai_socktype = SOCK_STREAM;
2630 * Handle numeric IPv4 and IPv6 addresses
2632 rc = inet_pton(AF_INET, host, &serveraddr);
2633 if (rc == 1) { /* dotted quad */
2634 hints.ai_family = AF_INET;
2635 hints.ai_flags |= AI_NUMERICHOST;
2638 rc = inet_pton(AF_INET6, host, &serveraddr);
2639 if (rc == 1) { /* IPv6 address */
2640 hints.ai_family = AF_INET6;
2641 hints.ai_flags |= AI_NUMERICHOST;
2645 /* Begin the connection process */
2647 rc = getaddrinfo(host, service, &hints, &res);
2653 * Try all available addresses until we connect to one or until we run out.
2655 for (ai = res; ai != NULL; ai = ai->ai_next) {
2656 sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
2657 if (sock < 0) return(-1);
2659 rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
2661 return(sock); /* Connected! */
2664 close(sock); /* Failed. Close the socket to avoid fd leak! */
2676 * Connect to a Citadel on the local host using a unix domain socket
2678 static int uds_connectsock(int *isLocal, char *sockpath)
2680 struct sockaddr_un addr;
2683 memset(&addr, 0, sizeof(addr));
2684 addr.sun_family = AF_UNIX;
2685 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2687 s = socket(AF_UNIX, SOCK_STREAM, 0);
2692 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2703 * input binary data from socket
2705 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2707 unsigned int len, rlen;
2709 #if defined(HAVE_OPENSSL)
2711 serv_read_ssl(ipc, buf, bytes);
2716 while (len < bytes) {
2717 rlen = read(ipc->sock, &buf[len], bytes - len);
2719 connection_died(ipc, 0);
2728 * send binary to server
2730 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2732 unsigned int bytes_written = 0;
2735 #if defined(HAVE_OPENSSL)
2737 serv_write_ssl(ipc, buf, nbytes);
2741 while (bytes_written < nbytes) {
2742 retval = write(ipc->sock, &buf[bytes_written],
2743 nbytes - bytes_written);
2745 connection_died(ipc, 0);
2748 bytes_written += retval;
2755 * input binary data from encrypted connection
2757 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2763 while (len < bytes) {
2764 if (SSL_want_read(ipc->ssl)) {
2765 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2766 error_printf("SSL_write in serv_read:\n");
2767 ERR_print_errors_fp(stderr);
2770 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2774 errval = SSL_get_error(ipc->ssl, rlen);
2775 if (errval == SSL_ERROR_WANT_READ ||
2776 errval == SSL_ERROR_WANT_WRITE) {
2781 Not sure why we'd want to handle these error codes any differently,
2782 but this definitely isn't the way to handle them. Someone must have
2783 naively assumed that we could fall back to unencrypted communications,
2784 but all it does is just recursively blow the stack.
2785 if (errval == SSL_ERROR_ZERO_RETURN ||
2786 errval == SSL_ERROR_SSL) {
2787 serv_read(ipc, &buf[len], bytes - len);
2791 error_printf("SSL_read in serv_read: %s\n",
2792 ERR_reason_error_string(ERR_peek_error()));
2793 connection_died(ipc, 1);
2802 * send binary to server encrypted
2804 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2806 unsigned int bytes_written = 0;
2810 while (bytes_written < nbytes) {
2811 if (SSL_want_write(ipc->ssl)) {
2812 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2813 error_printf("SSL_read in serv_write:\n");
2814 ERR_print_errors_fp(stderr);
2817 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2818 nbytes - bytes_written);
2822 errval = SSL_get_error(ipc->ssl, retval);
2823 if (errval == SSL_ERROR_WANT_READ ||
2824 errval == SSL_ERROR_WANT_WRITE) {
2828 if (errval == SSL_ERROR_ZERO_RETURN ||
2829 errval == SSL_ERROR_SSL) {
2830 serv_write(ipc, &buf[bytes_written],
2831 nbytes - bytes_written);
2834 error_printf("SSL_write in serv_write: %s\n",
2835 ERR_reason_error_string(ERR_peek_error()));
2836 connection_died(ipc, 1);
2839 bytes_written += retval;
2844 #ifdef THREADED_CLIENT
2845 static void ssl_lock(int mode, int n, const char *file, int line)
2847 if (mode & CRYPTO_LOCK)
2848 pthread_mutex_lock(Critters[n]);
2850 pthread_mutex_unlock(Critters[n]);
2852 #endif /* THREADED_CLIENT */
2855 static void CtdlIPC_init_OpenSSL(void)
2858 const SSL_METHOD *ssl_method;
2861 /* already done init */
2870 SSL_load_error_strings();
2871 SSLeay_add_ssl_algorithms();
2873 /* Set up the SSL context in which we will oeprate */
2874 ssl_method = SSLv23_client_method();
2875 ssl_ctx = SSL_CTX_new(ssl_method);
2877 error_printf("SSL_CTX_new failed: %s\n",
2878 ERR_reason_error_string(ERR_get_error()));
2881 /* Any reasonable cipher we can get */
2882 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2883 error_printf("No ciphers available for encryption\n");
2886 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2888 /* Load DH parameters into the context */
2891 error_printf("Can't allocate a DH object: %s\n",
2892 ERR_reason_error_string(ERR_get_error()));
2895 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2896 error_printf("Can't assign DH_P: %s\n",
2897 ERR_reason_error_string(ERR_get_error()));
2901 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2902 error_printf("Can't assign DH_G: %s\n",
2903 ERR_reason_error_string(ERR_get_error()));
2908 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2911 #ifdef THREADED_CLIENT
2912 /* OpenSSL requires callbacks for threaded clients */
2913 CRYPTO_set_locking_callback(ssl_lock);
2914 CRYPTO_set_id_callback(id_callback);
2916 /* OpenSSL requires us to do semaphores for threaded clients */
2917 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2919 perror("malloc failed");
2922 for (a = 0; a < CRYPTO_num_locks(); a++) {
2923 Critters[a] = malloc(sizeof (pthread_mutex_t));
2925 perror("malloc failed");
2928 pthread_mutex_init(Critters[a], NULL);
2931 #endif /* THREADED_CLIENT */
2936 #ifdef THREADED_CLIENT
2937 static unsigned long id_callback(void) {
2938 return (unsigned long)pthread_self();
2940 #endif /* THREADED_CLIENT */
2941 #endif /* HAVE_OPENSSL */
2945 ReadNetworkChunk(CtdlIPC* ipc)
2962 FD_SET(ipc->sock, &read_fd);
2963 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2965 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2969 *(ipc->BufPtr) = '\0';
2970 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2971 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2973 ipc->BufPtr[n]='\0';
2981 if (!(errno == EINTR || errno == EAGAIN))
2982 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2988 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2990 ipc->BufPtr[n]='\0';
2995 connection_died(ipc, 0);
3003 * input string from socket - implemented in terms of serv_read()
3007 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3010 char *aptr, *bptr, *aeptr, *beptr;
3012 // error_printf("---\n");
3015 #if defined(HAVE_OPENSSL)
3018 /* Read one character at a time. */
3020 serv_read(ipc, &buf[i], 1);
3021 if (buf[i] == '\n' || i == (SIZ-1))
3025 /* If we got a long line, discard characters until the newline. */
3027 while (buf[i] != '\n')
3028 serv_read(ipc, &buf[i], 1);
3030 /* Strip the trailing newline (and carriage return, if present) */
3031 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3032 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3037 if (ipc->Buf == NULL)
3040 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3042 ipc->BufPtr = ipc->Buf;
3046 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3047 if (ipc->BufUsed == 0)
3048 ReadNetworkChunk(ipc);
3050 //// if (ipc->BufUsed != 0) while (1)
3056 aeptr = ipc->Buf + ipc->BufSize;
3057 while ((aptr < aeptr) &&
3061 *(bptr++) = *(aptr++);
3062 if ((*aptr == '\n') && (aptr < aeptr))
3064 /* Terminate it right, remove the line breaks */
3065 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3067 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3070 // fprintf(stderr, "parsing %d %d %d - %d %d %d %s\n", ipc->BufPtr - ipc->Buf, aptr - ipc->BufPtr, ipc->BufUsed , *aptr, *(aptr-1), *(aptr+1), buf);
3071 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3074 /* is there more in the buffer we need to read later? */
3075 if (ipc->Buf + ipc->BufUsed > aptr)
3082 ipc->BufPtr = ipc->Buf;
3084 // error_printf("----bla6\n");
3087 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3088 else if ((ipc->BufPtr != ipc->Buf) &&
3089 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3091 size_t NewBufSize = ipc->BufSize * 2;
3092 int delta = (ipc->BufPtr - ipc->Buf);
3095 /* if the line would end after our buffer, we should use a bigger buffer. */
3096 NewBuf = (char *)malloc (NewBufSize + 10);
3097 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3099 ipc->Buf = ipc->BufPtr = NewBuf;
3100 ipc->BufUsed -= delta;
3101 ipc->BufSize = NewBufSize;
3103 if (ReadNetworkChunk(ipc) <0)
3105 // error_printf("----bla\n");
3109 /// error_printf("----bl45761%s\nipc->BufUsed");
3111 // error_printf("----bla1\n");
3114 #else /* CHUNKED_READ */
3116 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3120 /* Read one character at a time. */
3122 serv_read(ipc, &buf[i], 1);
3123 if (buf[i] == '\n' || i == (SIZ-1))
3127 /* If we got a long line, discard characters until the newline. */
3129 while (buf[i] != '\n')
3130 serv_read(ipc, &buf[i], 1);
3132 /* Strip the trailing newline (and carriage return, if present) */
3133 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3134 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3138 #endif /* CHUNKED_READ */
3141 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3143 CtdlIPC_getline(ipc, buf);
3147 * send line to server - implemented in terms of serv_write()
3149 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3155 cmd = malloc(len + 2);
3157 /* This requires no extra memory */
3158 serv_write(ipc, buf, len);
3159 serv_write(ipc, "\n", 1);
3161 /* This is network-optimized */
3162 strncpy(cmd, buf, len);
3163 strcpy(cmd + len, "\n");
3164 serv_write(ipc, cmd, len + 1);
3168 ipc->last_command_sent = time(NULL);
3171 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3173 CtdlIPC_putline(ipc, buf);
3180 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3188 ipc = ialloc(CtdlIPC);
3192 #if defined(HAVE_OPENSSL)
3194 CtdlIPC_init_OpenSSL();
3196 #if defined(HAVE_PTHREAD_H)
3197 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3199 ipc->sock = -1; /* Not connected */
3200 ipc->isLocal = 0; /* Not local, of course! */
3201 ipc->downloading = 0;
3203 ipc->last_command_sent = 0L;
3204 ipc->network_status_cb = NULL;
3209 strcpy(cithost, DEFAULT_HOST); /* default host */
3210 strcpy(citport, DEFAULT_PORT); /* default port */
3212 /* Allow caller to supply our values (Windows) */
3213 if (hostbuf && strlen(hostbuf) > 0)
3214 strcpy(cithost, hostbuf);
3215 if (portbuf && strlen(portbuf) > 0)
3216 strcpy(citport, portbuf);
3218 /* Read host/port from command line if present */
3219 for (a = 0; a < argc; ++a) {
3222 } else if (a == 1) {
3223 strcpy(cithost, argv[a]);
3224 } else if (a == 2) {
3225 strcpy(citport, argv[a]);
3227 error_printf("%s: usage: ",argv[0]);
3228 error_printf("%s [host] [port] ",argv[0]);
3235 if ((!strcmp(cithost, "localhost"))
3236 || (!strcmp(cithost, "127.0.0.1"))) {
3240 /* If we're using a unix domain socket we can do a bunch of stuff */
3241 if (!strcmp(cithost, UDS)) {
3242 if (!strcasecmp(citport, DEFAULT_PORT)) {
3243 snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
3246 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3248 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3249 if (ipc->sock == -1) {
3253 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3254 if (portbuf != NULL) strcpy(portbuf, sockpath);
3255 strcpy(ipc->ip_hostname, "");
3256 strcpy(ipc->ip_address, "");
3260 ipc->sock = tcp_connectsock(cithost, citport);
3261 if (ipc->sock == -1) {
3267 /* Learn the actual network identity of the host to which we are connected */
3269 struct sockaddr_in6 clientaddr;
3270 unsigned int addrlen = sizeof(clientaddr);
3272 ipc->ip_hostname[0] = 0;
3273 ipc->ip_address[0] = 0;
3275 getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
3276 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3277 ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
3279 getnameinfo((struct sockaddr *)&clientaddr, addrlen,
3280 ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
3283 /* stuff other things elsewhere */
3285 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3286 if (portbuf != NULL) strcpy(portbuf, citport);
3292 * Disconnect and delete the IPC class (destructor)
3294 void CtdlIPC_delete(CtdlIPC* ipc)
3298 SSL_shutdown(ipc->ssl);
3303 if (ipc->sock > -1) {
3304 shutdown(ipc->sock, 2); /* Close it up */
3307 if (ipc->Buf != NULL)
3316 * Disconnect and delete the IPC class (destructor)
3317 * Also NULLs out the pointer
3319 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3321 CtdlIPC_delete(*pipc);
3327 * return the file descriptor of the server socket so we can select() on it.
3329 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3332 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3339 * return one character
3341 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3344 char CtdlIPC_get(CtdlIPC* ipc)
3349 serv_read(ipc, buf, 1);