3 * Copyright (c) 1987-2009 by the citadel.org team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #if TIME_WITH_SYS_TIME
22 # include <sys/time.h>
26 # include <sys/time.h>
33 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <arpa/inet.h>
42 #include <netinet/in.h>
46 #ifdef THREADED_CLIENT
49 #include <libcitadel.h>
51 #include "citadel_ipc.h"
52 #include "citadel_decls.h"
53 #include "citadel_dirs.h"
54 #ifdef THREADED_CLIENT
55 pthread_mutex_t rwlock;
59 static SSL_CTX *ssl_ctx;
62 #ifdef THREADED_CLIENT
63 pthread_mutex_t **Critters; /* Things that need locking */
64 #endif /* THREADED_CLIENT */
66 #endif /* HAVE_OPENSSL */
69 #define INADDR_NONE 0xffffffff
72 static void (*status_hook)(char *s) = NULL;
74 void setCryptoStatusHook(void (*hook)(char *s)) {
78 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
79 ipc->network_status_cb = hook;
83 char instant_msgs = 0;
86 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
87 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
89 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
90 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
91 static void endtls(SSL *ssl);
92 #ifdef THREADED_CLIENT
93 static unsigned long id_callback(void);
94 #endif /* THREADED_CLIENT */
95 #endif /* HAVE_OPENSSL */
96 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
97 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
101 const char *svn_revision(void);
104 * Does nothing. The server should always return 200.
106 int CtdlIPCNoop(CtdlIPC *ipc)
110 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
115 * Does nothing interesting. The server should always return 200
116 * along with your string.
118 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
124 if (!cret) return -2;
126 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
129 sprintf(aaa, "ECHO %s", arg);
130 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
137 * Asks the server to close the connecction.
138 * Should always return 200.
140 int CtdlIPCQuit(CtdlIPC *ipc)
142 register int ret = 221; /* Default to successful quit */
146 if (ipc->sock > -1) {
147 CtdlIPC_putline(ipc, "QUIT");
148 CtdlIPC_getline(ipc, aaa);
153 SSL_shutdown(ipc->ssl);
157 shutdown(ipc->sock, 2); /* Close connection; we're dead */
165 * Asks the server to log out. Should always return 200, even if no user
166 * was logged in. The user will not be logged in after this!
168 int CtdlIPCLogout(CtdlIPC *ipc)
174 CtdlIPC_putline(ipc, "LOUT");
175 CtdlIPC_getline(ipc, aaa);
183 * First stage of authentication - pass the username. Returns 300 if the
184 * username is able to log in, with the username correctly spelled in cret.
185 * Returns various 500 error codes if the user doesn't exist, etc.
187 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
192 if (!username) return -2;
193 if (!cret) return -2;
195 aaa = (char *)malloc((size_t)(strlen(username) + 6));
198 sprintf(aaa, "USER %s", username);
199 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
206 * Second stage of authentication - provide password. The server returns
207 * 200 and several arguments in cret relating to the user's account.
209 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
214 if (!passwd) return -2;
215 if (!cret) return -2;
217 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
220 sprintf(aaa, "PASS %s", passwd);
221 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
228 * Second stage of authentication - provide password. The server returns
229 * 200 and several arguments in cret relating to the user's account.
231 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
236 if (!response) return -2;
237 if (!cret) return -2;
239 aaa = (char *)malloc((size_t)(strlen(response) + 6));
242 sprintf(aaa, "PAS2 %s", response);
243 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
250 * Create a new user. This returns 200 plus the same arguments as TryPassword
251 * if selfservice is nonzero, unless there was a problem creating the account.
252 * If selfservice is zero, creates a new user but does not log out the existing
253 * user - intended for use by system administrators to create accounts on
254 * behalf of other users.
256 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
261 if (!username) return -2;
262 if (!cret) return -2;
264 aaa = (char *)malloc((size_t)(strlen(username) + 6));
267 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
268 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
275 * Changes the user's password. Returns 200 if changed, errors otherwise.
277 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
282 if (!passwd) return -2;
283 if (!cret) return -2;
285 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
288 sprintf(aaa, "SETP %s", passwd);
289 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
296 /* Caller must free the march list */
297 /* Room types are defined in enum RoomList; keep these in sync! */
298 /* floor is -1 for all, or floornum */
299 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
302 struct march *march = NULL;
303 static char *proto[] =
304 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
309 if (!listing) return -2;
310 if (*listing) return -2; /* Free the listing first */
311 if (!cret) return -2;
312 /* if (which < 0 || which > 4) return -2; */
313 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
315 sprintf(aaa, "%s %d", proto[which], floor);
316 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
317 if (ret / 100 == 1) {
320 while (bbb && strlen(bbb)) {
323 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
325 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
326 mptr = (struct march *) malloc(sizeof (struct march));
329 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
330 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
331 mptr->march_floor = (char) extract_int(aaa, 2);
332 mptr->march_order = (char) extract_int(aaa, 3);
333 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
334 mptr->march_access = (char) extract_int(aaa, 5);
341 while (mptr2->next != NULL)
355 /* Caller must free the struct ctdluser; caller may pass an existing one */
356 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
360 if (!cret) return -2;
361 if (!uret) return -2;
362 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
363 if (!*uret) return -1;
365 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
366 if (ret / 100 == 2) {
367 uret[0]->USscreenwidth = extract_int(cret, 0);
368 uret[0]->USscreenheight = extract_int(cret, 1);
369 uret[0]->flags = extract_int(cret, 2);
376 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
380 if (!uret) return -2;
381 if (!cret) return -2;
383 sprintf(aaa, "SETU %d|%d|%d",
384 uret->USscreenwidth, uret->USscreenheight,
386 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
391 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
396 if (!oldname) return -2;
397 if (!newname) return -2;
398 if (!cret) return -2;
400 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
401 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
407 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
408 struct ctdlipcroom **rret, char *cret)
413 if (!cret) return -2;
414 if (!rret) return -2;
415 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
416 if (!*rret) return -1;
419 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
424 sprintf(aaa, "GOTO %s|%s", room, passwd);
426 aaa = (char *)malloc(strlen(room) + 6);
431 sprintf(aaa, "GOTO %s", room);
433 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
434 if (ret / 100 == 2) {
435 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
436 rret[0]->RRunread = extract_long(cret, 1);
437 rret[0]->RRtotal = extract_long(cret, 2);
438 rret[0]->RRinfoupdated = extract_int(cret, 3);
439 rret[0]->RRflags = extract_int(cret, 4);
440 rret[0]->RRhighest = extract_long(cret, 5);
441 rret[0]->RRlastread = extract_long(cret, 6);
442 rret[0]->RRismailbox = extract_int(cret, 7);
443 rret[0]->RRaide = extract_int(cret, 8);
444 rret[0]->RRnewmail = extract_long(cret, 9);
445 rret[0]->RRfloor = extract_int(cret, 10);
446 rret[0]->RRflags2 = extract_int(cret, 14);
457 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
458 /* whicharg is number of messages, applies to last, first, gt, lt */
459 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
460 const char *mtemplate, unsigned long **mret, char *cret)
463 register unsigned long count = 0;
464 static char *proto[] =
465 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
470 if (!cret) return -2;
471 if (!mret) return -2;
472 if (*mret) return -2;
473 if (which < 0 || which > 6) return -2;
476 sprintf(aaa, "MSGS %s||%d", proto[which],
477 (mtemplate) ? 1 : 0);
479 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
480 (mtemplate) ? 1 : 0);
481 if (mtemplate) count = strlen(mtemplate);
482 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
486 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
489 while (bbb && strlen(bbb)) {
490 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
491 remove_token(bbb, 0, '\n');
492 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
493 sizeof (unsigned long)));
495 (*mret)[count++] = atol(aaa);
507 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
508 struct ctdlipcmessage **mret, char *cret)
514 int multipart_hunting = 0;
515 char multipart_prefix[128];
518 if (!cret) return -1;
519 if (!mret) return -1;
520 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
521 if (!*mret) return -1;
522 if (!msgnum) return -1;
524 strcpy(encoding, "");
525 strcpy(mret[0]->content_type, "");
526 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
527 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
528 if (ret / 100 == 1) {
530 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
531 while (strlen(bbb) > 4 && bbb[4] == '=') {
532 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
533 remove_token(bbb, 0, '\n');
535 if (!strncasecmp(aaa, "nhdr=yes", 8))
537 else if (!strncasecmp(aaa, "from=", 5))
538 safestrncpy(mret[0]->author, &aaa[5], SIZ);
539 else if (!strncasecmp(aaa, "type=", 5))
540 mret[0]->type = atoi(&aaa[5]);
541 else if (!strncasecmp(aaa, "msgn=", 5))
542 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
543 else if (!strncasecmp(aaa, "subj=", 5))
544 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
545 else if (!strncasecmp(aaa, "rfca=", 5))
546 safestrncpy(mret[0]->email, &aaa[5], SIZ);
547 else if (!strncasecmp(aaa, "hnod=", 5))
548 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
549 else if (!strncasecmp(aaa, "room=", 5))
550 safestrncpy(mret[0]->room, &aaa[5], SIZ);
551 else if (!strncasecmp(aaa, "node=", 5))
552 safestrncpy(mret[0]->node, &aaa[5], SIZ);
553 else if (!strncasecmp(aaa, "rcpt=", 5))
554 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
555 else if (!strncasecmp(aaa, "wefw=", 5))
556 safestrncpy(mret[0]->references, &aaa[5], SIZ);
557 else if (!strncasecmp(aaa, "time=", 5))
558 mret[0]->time = atol(&aaa[5]);
560 /* Multipart/alternative prefix & suffix strings help
561 * us to determine which part we want to download.
563 else if (!strncasecmp(aaa, "pref=", 5)) {
564 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
565 if (!strcasecmp(multipart_prefix,
566 "multipart/alternative")) {
570 else if (!strncasecmp(aaa, "suff=", 5)) {
571 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
572 if (!strcasecmp(multipart_prefix,
573 "multipart/alternative")) {
578 else if (!strncasecmp(aaa, "part=", 5)) {
579 struct parts *ptr, *chain;
581 ptr = (struct parts *)calloc(1, sizeof (struct parts));
584 /* Fill the buffers for the caller */
585 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
586 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
587 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
588 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
589 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
590 ptr->length = extract_long(&aaa[5], 5);
591 if (!mret[0]->attachments)
592 mret[0]->attachments = ptr;
594 chain = mret[0]->attachments;
600 /* Now handle multipart/alternative */
601 if (multipart_hunting > 0) {
602 if ( (!strcasecmp(ptr->mimetype,
604 || (!strcasecmp(ptr->mimetype,
606 strcpy(mret[0]->mime_chosen,
614 /* Eliminate "text\n" */
615 remove_token(bbb, 0, '\n');
617 /* If doing a MIME thing, pull out the extra headers */
620 if (!strncasecmp(bbb, "Content-type:", 13)) {
621 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
622 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
623 striplt(mret[0]->content_type);
625 /* strip out ";charset=" portion. FIXME do something with
626 * the charset (like... convert it) instead of just throwing
629 if (strstr(mret[0]->content_type, ";") != NULL) {
630 strcpy(strstr(mret[0]->content_type, ";"), "");
634 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
635 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
636 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
637 striplt(mret[0]->mime_chosen);
639 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
640 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
641 strcpy(encoding, &encoding[26]);
644 remove_token(bbb, 0, '\n');
645 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
646 remove_token(bbb, 0, '\n');
653 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
655 int bytes_decoded = 0;
656 ccc = malloc(strlen(bbb) + 32768);
657 if (!strcasecmp(encoding, "base64")) {
658 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
660 else if (!strcasecmp(encoding, "quoted-printable")) {
661 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
663 ccc[bytes_decoded] = 0;
668 /* FIXME: Strip trailing whitespace */
669 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
672 bbb = (char *)realloc(bbb, 1);
682 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
687 if (!cret) return -2;
688 if (!listing) return -2;
689 if (*listing) return -2;
691 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
697 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
701 char *listing = NULL;
704 if (!cret) return -2;
706 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
707 if (ret / 100 == 1) {
710 while (*listing && strlen(listing)) {
711 extract_token(buf, listing, 0, '\n', sizeof buf);
712 remove_token(listing, 0, '\n');
714 case 0: ipc->ServInfo.pid = atoi(buf);
716 case 1: strcpy(ipc->ServInfo.nodename,buf);
718 case 2: strcpy(ipc->ServInfo.humannode,buf);
720 case 3: strcpy(ipc->ServInfo.fqdn,buf);
722 case 4: strcpy(ipc->ServInfo.software,buf);
724 case 5: ipc->ServInfo.rev_level = atoi(buf);
726 case 6: strcpy(ipc->ServInfo.site_location,buf);
728 case 7: strcpy(ipc->ServInfo.sysadm,buf);
730 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
732 case 10: ipc->ServInfo.ok_floors = atoi(buf);
734 case 11: ipc->ServInfo.paging_level = atoi(buf);
736 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
738 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
740 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
742 case 16: strcpy(ipc->ServInfo.default_cal_zone, buf);
744 case 17: ipc->ServInfo.load_avg = atof(buf);
746 case 18: ipc->ServInfo.worker_avg = atof(buf);
748 case 19: ipc->ServInfo.thread_count = atoi(buf);
750 case 20: ipc->ServInfo.has_sieve = atoi(buf);
752 case 21: ipc->ServInfo.fulltext_enabled = atoi(buf);
754 case 22: strcpy(ipc->ServInfo.svn_revision, buf);
760 if (listing) free(listing);
766 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
771 if (!cret) return -2;
772 if (!listing) return -2;
773 if (*listing) return -2;
775 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
781 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
783 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
788 if (!cret) return -2;
791 sprintf(aaa, "SLRP %ld", msgnum);
794 sprintf(aaa, "SLRP HIGHEST");
796 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
802 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
807 if (!cret) return -2;
808 if (!username) return -2;
810 aaa = (char *)malloc(strlen(username) + 6);
813 sprintf(aaa, "INVT %s", username);
814 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
821 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
826 if (!cret) return -1;
827 if (!username) return -1;
829 aaa = (char *)malloc(strlen(username) + 6);
831 sprintf(aaa, "KICK %s", username);
832 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
839 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
843 if (!cret) return -2;
844 if (!qret) return -2;
845 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
846 if (!*qret) return -1;
848 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
849 if (ret / 100 == 2) {
850 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
851 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
852 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
853 qret[0]->QRflags = extract_int(cret, 3);
854 qret[0]->QRfloor = extract_int(cret, 4);
855 qret[0]->QRorder = extract_int(cret, 5);
856 qret[0]->QRdefaultview = extract_int(cret, 6);
857 qret[0]->QRflags2 = extract_int(cret, 7);
864 /* set forget to kick all users out of room */
865 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
870 if (!cret) return -2;
871 if (!qret) return -2;
873 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
874 strlen(qret->QRdirname) + 64);
877 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
878 qret->QRname, qret->QRpasswd, qret->QRdirname,
879 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
880 qret->QRdefaultview, qret->QRflags2);
881 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
888 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
890 if (!cret) return -1;
892 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
897 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
902 if (!cret) return -2;
903 if (!username) return -2;
905 aaa = (char *)malloc(strlen(username) + 6);
908 sprintf(aaa, "SETA %s", username);
909 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
916 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, struct ctdlipcmessage *mr, char *cret)
922 if (!cret) return -2;
925 if (mr->references) {
926 for (ptr=mr->references; *ptr != 0; ++ptr) {
927 if (*ptr == '|') *ptr = '!';
931 snprintf(cmd, sizeof cmd,
932 "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
933 mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
934 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
936 if ((flag == 0) && (subject_required != NULL)) {
937 /* Is the server strongly recommending that the user enter a message subject? */
938 if ((cret[3] != '\0') && (cret[4] != '\0')) {
939 *subject_required = extract_int(&cret[4], 1);
949 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
953 if (!cret) return -2;
954 if (!iret) return -2;
955 if (*iret) return -2;
957 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
962 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
966 if (!cret) return -2;
967 if (!msgnum) return -2;
969 sprintf(aaa, "DELE %ld", msgnum);
970 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
975 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
980 if (!cret) return -2;
981 if (!destroom) return -2;
982 if (!msgnum) return -2;
984 aaa = (char *)malloc(strlen(destroom) + 28);
987 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
988 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
995 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
999 if (!cret) return -2;
1001 sprintf(aaa, "KILL %d", for_real);
1002 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1007 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
1008 const char *password, int floor, char *cret)
1013 if (!cret) return -2;
1014 if (!roomname) return -2;
1017 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
1018 if (!aaa) return -1;
1019 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
1022 aaa = (char *)malloc(strlen(roomname) + 40);
1023 if (!aaa) return -1;
1024 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
1027 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1034 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
1036 if (!cret) return -2;
1038 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1043 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1049 if (!cret) return -2;
1050 if (!mret) return -2;
1051 if (*mret) return -2;
1052 if (!message) return -2;
1054 aaa = (char *)malloc(strlen(message) + 6);
1055 if (!aaa) return -1;
1057 sprintf(aaa, "MESG %s", message);
1058 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1065 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1067 if (!cret) return -2;
1069 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1074 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1080 if (!cret) return -2;
1081 if (!rret) return -2;
1082 if (*rret) return -2;
1085 aaa = (char *)malloc(strlen(username) + 6);
1087 aaa = (char *)malloc(12);
1088 if (!aaa) return -1;
1091 sprintf(aaa, "GREG %s", username);
1093 sprintf(aaa, "GREG _SELF_");
1094 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1101 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1106 if (!cret) return -2;
1107 if (!username) return -2;
1108 if (axlevel < 0 || axlevel > 7) return -2;
1110 aaa = (char *)malloc(strlen(username) + 17);
1111 if (!aaa) return -1;
1113 sprintf(aaa, "VALI %s|%d", username, axlevel);
1114 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1121 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1125 if (!cret) return -1;
1126 if (!info) return -1;
1128 sprintf(aaa, "EINF %d", for_real);
1129 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1134 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1140 if (!cret) return -1;
1141 if (!listing) return -1;
1142 if (*listing) return -1;
1143 if (!searchstring) return -1;
1145 cmd = malloc(strlen(searchstring) + 10);
1146 sprintf(cmd, "LIST %s", searchstring);
1148 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1155 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1157 if (!cret) return -1;
1158 if (!info) return -1;
1160 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1166 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1170 if (!cret) return -1;
1171 if (!chek) return -1;
1173 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1174 if (ret / 100 == 2) {
1175 chek->newmail = extract_long(cret, 0);
1176 chek->needregis = extract_int(cret, 1);
1177 chek->needvalid = extract_int(cret, 2);
1184 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1189 if (!cret) return -2;
1190 if (!filename) return -2;
1192 aaa = (char *)malloc(strlen(filename) + 6);
1193 if (!aaa) return -1;
1195 sprintf(aaa, "DELF %s", filename);
1196 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1203 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1208 if (!cret) return -2;
1209 if (!filename) return -2;
1210 if (!destroom) return -2;
1212 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1213 if (!aaa) return -1;
1215 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1216 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1223 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1228 if (!cret) return -1;
1229 if (!listing) return -1;
1230 if (*listing) return -1;
1232 *stamp = CtdlIPCServerTime(ipc, cret);
1234 *stamp = time(NULL);
1235 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1241 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1243 void (*progress_gauge_callback)
1244 (CtdlIPC*, unsigned long, unsigned long),
1253 if (!cret) return -2;
1254 if (!filename) return -2;
1255 if (!buf) return -2;
1256 if (*buf) return -2;
1257 if (ipc->downloading) return -2;
1259 aaa = (char *)malloc(strlen(filename) + 6);
1260 if (!aaa) return -1;
1262 sprintf(aaa, "OPEN %s", filename);
1263 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1265 if (ret / 100 == 2) {
1266 ipc->downloading = 1;
1267 bytes = extract_long(cret, 0);
1268 last_mod = extract_int(cret, 1);
1269 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1271 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1272 progress_gauge_callback, cret);
1274 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1275 progress_gauge_callback, cret);
1278 ret = CtdlIPCEndDownload(ipc, cret);
1280 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1281 filename, mimetype);
1288 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1290 void (*progress_gauge_callback)
1291 (CtdlIPC*, unsigned long, unsigned long),
1301 if (!cret) return -2;
1302 if (!buf) return -2;
1303 if (*buf) return -2;
1304 if (!part) return -2;
1305 if (!msgnum) return -2;
1306 if (ipc->downloading) return -2;
1308 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1309 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1310 if (ret / 100 == 2) {
1311 ipc->downloading = 1;
1312 bytes = extract_long(cret, 0);
1313 last_mod = extract_int(cret, 1);
1314 extract_token(filename, cret, 2, '|', sizeof filename);
1315 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1316 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1317 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1318 ret = CtdlIPCEndDownload(ipc, cret);
1320 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1321 filename, mimetype);
1328 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1329 void (*progress_gauge_callback)
1330 (CtdlIPC*, unsigned long, unsigned long),
1339 if (!cret) return -1;
1340 if (!buf) return -1;
1341 if (*buf) return -1;
1342 if (!filename) return -1;
1343 if (ipc->downloading) return -1;
1345 aaa = (char *)malloc(strlen(filename) + 6);
1346 if (!aaa) return -1;
1348 sprintf(aaa, "OIMG %s", filename);
1349 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1351 if (ret / 100 == 2) {
1352 ipc->downloading = 1;
1353 bytes = extract_long(cret, 0);
1354 last_mod = extract_int(cret, 1);
1355 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1356 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1357 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1358 ret = CtdlIPCEndDownload(ipc, cret);
1360 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1361 filename, mimetype);
1368 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1370 void (*progress_gauge_callback)
1371 (CtdlIPC*, unsigned long, unsigned long),
1377 char MimeTestBuf[64];
1378 const char *MimeType;
1381 if (!cret) return -1;
1382 if (!save_as) return -1;
1383 if (!comment) return -1;
1384 if (!path) return -1;
1385 if (!*path) return -1;
1386 if (ipc->uploading) return -1;
1388 uploadFP = fopen(path, "r");
1389 if (!uploadFP) return -2;
1391 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1396 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1397 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1398 if (!aaa) return -1;
1400 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1401 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1403 if (ret / 100 == 2) {
1405 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1406 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1414 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1415 const char *save_as,
1416 void (*progress_gauge_callback)
1417 (CtdlIPC*, unsigned long, unsigned long),
1423 char MimeTestBuf[64];
1424 const char *MimeType;
1427 if (!cret) return -1;
1428 if (!save_as) return -1;
1429 if (!path && for_real) return -1;
1430 if (!*path && for_real) return -1;
1431 if (ipc->uploading) return -1;
1433 aaa = (char *)malloc(strlen(save_as) + 17);
1434 if (!aaa) return -1;
1436 uploadFP = fopen(path, "r");
1437 if (!uploadFP) return -2;
1439 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1443 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1445 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1446 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1448 if (ret / 100 == 2 && for_real) {
1450 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1451 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1459 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1464 if (!cret) return -2;
1465 if (!username) return -2;
1467 aaa = (char *)malloc(strlen(username) + 6);
1468 if (!aaa) return -1;
1470 sprintf(aaa, "QUSR %s", username);
1471 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1478 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1482 if (!cret) return -2;
1483 if (!listing) return -2;
1484 if (*listing) return -2;
1486 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1491 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1496 if (!cret) return -2;
1497 if (!name) return -2;
1499 sprintf(aaa, "CFLR %s|%d", name, for_real);
1500 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1506 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1510 if (!cret) return -1;
1511 if (floornum < 0) return -1;
1513 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1514 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1519 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1524 if (!cret) return -2;
1525 if (!floorname) return -2;
1526 if (floornum < 0) return -2;
1528 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1529 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1537 * You only need to fill out hostname, the defaults will be used if any of the
1538 * other fields are not set properly.
1540 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1541 int revision, const char *software_name, const char *hostname,
1547 if (developerid < 0 || clientid < 0 || revision < 0 ||
1551 revision = REV_LEVEL - 600;
1552 software_name = "Citadel (libcitadel)";
1554 if (!hostname) return -2;
1556 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1557 if (!aaa) return -1;
1559 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1560 revision, software_name, hostname);
1561 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1568 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1574 if (!cret) return -2;
1575 if (!username) return -2;
1577 aaa = (char *)malloc(strlen(username) + 8);
1578 if (!aaa) return -1;
1581 sprintf(aaa, "SEXP %s|-", username);
1582 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1585 sprintf(aaa, "SEXP %s||", username);
1586 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1594 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1598 if (!cret) return -2;
1599 if (!listing) return -2;
1600 if (*listing) return -2;
1602 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1607 /* mode is 0 = enable, 1 = disable, 2 = status */
1608 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1612 if (!cret) return -2;
1614 sprintf(aaa, "DEXP %d", mode);
1615 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1620 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1622 if (!cret) return -2;
1623 if (!bio) return -2;
1625 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1631 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1637 if (!cret) return -2;
1638 if (!username) return -2;
1639 if (!listing) return -2;
1640 if (*listing) return -2;
1642 aaa = (char *)malloc(strlen(username) + 6);
1643 if (!aaa) return -1;
1645 sprintf(aaa, "RBIO %s", username);
1646 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1653 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1657 if (!cret) return -2;
1658 if (!listing) return -2;
1659 if (*listing) return -2;
1661 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1666 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1670 if (!cret) return -1;
1672 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1673 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1678 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1682 if (!cret) return -1;
1684 sprintf(aaa, "TERM %d", sid);
1685 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1690 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1692 if (!cret) return -1;
1694 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1699 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1703 if (!cret) return -1;
1705 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1706 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1711 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1717 if (!cret) return -2;
1718 if (!text) return -2;
1719 if (!filename) return -2;
1721 aaa = (char *)malloc(strlen(filename) + 6);
1722 if (!aaa) return -1;
1724 sprintf(aaa, "EMSG %s", filename);
1725 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1732 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1737 if (!cret) return -2;
1738 if (!hostname) return -2;
1740 aaa = (char *)malloc(strlen(hostname) + 6);
1741 if (!aaa) return -1;
1743 sprintf(aaa, "HCHG %s", hostname);
1744 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1751 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1756 if (!cret) return -2;
1757 if (!roomname) return -2;
1759 aaa = (char *)malloc(strlen(roomname) + 6);
1760 if (!aaa) return -1;
1762 sprintf(aaa, "RCHG %s", roomname);
1763 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1770 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1775 if (!cret) return -2;
1776 if (!username) return -2;
1778 aaa = (char *)malloc(strlen(username) + 6);
1779 if (!aaa) return -1;
1781 sprintf(aaa, "UCHG %s", username);
1782 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1789 /* This function returns the actual server time reported, or 0 if error */
1790 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1792 register time_t tret;
1795 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1796 if (ret / 100 == 2) {
1797 tret = extract_long(cret, 0);
1806 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1807 struct ctdluser **uret, char *cret)
1812 if (!cret) return -2;
1813 if (!uret) return -2;
1814 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1815 if (!*uret) return -1;
1817 sprintf(aaa, "AGUP %s", who);
1818 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1820 if (ret / 100 == 2) {
1821 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1822 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1823 uret[0]->flags = extract_int(cret, 2);
1824 uret[0]->timescalled = extract_long(cret, 3);
1825 uret[0]->posted = extract_long(cret, 4);
1826 uret[0]->axlevel = extract_int(cret, 5);
1827 uret[0]->usernum = extract_long(cret, 6);
1828 uret[0]->lastcall = extract_long(cret, 7);
1829 uret[0]->USuserpurge = extract_int(cret, 8);
1836 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1841 if (!cret) return -2;
1842 if (!uret) return -2;
1844 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1845 if (!aaa) return -1;
1847 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1848 uret->fullname, uret->password, uret->flags,
1849 uret->timescalled, uret->posted, uret->axlevel,
1850 uret->usernum, uret->lastcall, uret->USuserpurge);
1851 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1858 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1859 /* caller must free the struct ExpirePolicy */
1860 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1861 struct ExpirePolicy **policy, char *cret)
1863 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1867 if (!cret) return -2;
1868 if (!policy) return -2;
1869 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1870 if (!*policy) return -1;
1871 if (which < 0 || which > 3) return -2;
1873 sprintf(cmd, "GPEX %s", proto[which]);
1874 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1875 if (ret / 100 == 2) {
1876 policy[0]->expire_mode = extract_int(cret, 0);
1877 policy[0]->expire_value = extract_int(cret, 1);
1884 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1885 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1886 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1887 struct ExpirePolicy *policy, char *cret)
1890 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1892 if (!cret) return -2;
1893 if (which < 0 || which > 3) return -2;
1894 if (!policy) return -2;
1895 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1896 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1898 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1899 policy->expire_mode, policy->expire_value);
1900 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1905 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1909 if (!cret) return -2;
1910 if (!listing) return -2;
1911 if (*listing) return -2;
1913 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1914 listing, &bytes, cret);
1919 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1921 if (!cret) return -2;
1922 if (!listing) return -2;
1924 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1930 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1931 char **listing, char *cret)
1937 if (!cret) return -2;
1938 if (!mimetype) return -2;
1939 if (!listing) return -2;
1940 if (*listing) return -2;
1942 aaa = malloc(strlen(mimetype) + 13);
1943 if (!aaa) return -1;
1944 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1945 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1946 listing, &bytes, cret);
1953 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1954 const char *listing, char *cret)
1959 if (!cret) return -2;
1960 if (!mimetype) return -2;
1961 if (!listing) return -2;
1963 aaa = malloc(strlen(mimetype) + 13);
1964 if (!aaa) return -1;
1965 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1966 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1974 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1978 if (!cret) return -2;
1979 if (!listing) return -2;
1980 if (*listing) return -2;
1982 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1983 listing, &bytes, cret);
1988 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1990 if (!cret) return -2;
1991 if (!listing) return -2;
1993 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1999 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
2003 if (!cret) return -2;
2004 if (session < 0) return -2;
2006 sprintf(aaa, "REQT %d", session);
2007 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2012 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
2016 if (!cret) return -2;
2017 if (msgnum < 0) return -2;
2019 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
2020 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2025 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2034 /* New SSL object */
2035 temp_ssl = SSL_new(ssl_ctx);
2037 error_printf("SSL_new failed: %s\n",
2038 ERR_reason_error_string(ERR_get_error()));
2041 /* Pointless flag waving */
2042 #if SSLEAY_VERSION_NUMBER >= 0x0922
2043 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
2046 if (!access(EGD_POOL, F_OK))
2049 if (!RAND_status()) {
2050 error_printf("PRNG not properly seeded\n");
2054 /* Associate network connection with SSL object */
2055 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2056 error_printf("SSL_set_fd failed: %s\n",
2057 ERR_reason_error_string(ERR_get_error()));
2061 if (status_hook != NULL)
2062 status_hook("Requesting encryption...\r");
2064 /* Ready to start SSL/TLS */
2066 CtdlIPC_putline(ipc, "STLS");
2067 CtdlIPC_getline(ipc, buf);
2068 if (buf[0] != '2') {
2069 error_printf("Server can't start TLS: %s\n", buf);
2073 r = CtdlIPCGenericCommand(ipc,
2074 "STLS", NULL, 0, NULL, NULL, cret);
2076 error_printf("Server can't start TLS: %s\n", buf);
2081 /* Do SSL/TLS handshake */
2082 if ((a = SSL_connect(temp_ssl)) < 1) {
2083 error_printf("SSL_connect failed: %s\n",
2084 ERR_reason_error_string(ERR_get_error()));
2088 ipc->ssl = temp_ssl;
2090 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2094 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2095 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2096 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2097 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2103 #endif /* HAVE_OPENSSL */
2108 static void endtls(SSL *ssl)
2119 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2124 if (!address) return -2;
2125 if (!cret) return -2;
2127 aaa = (char *)malloc(strlen(address) + 6);
2128 if (!aaa) return -1;
2130 sprintf(aaa, "QDIR %s", address);
2131 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2138 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2142 if (!cret) return -2;
2143 sprintf(aaa, "IPGM %d", secret);
2144 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2149 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2153 if (!cret) return -2;
2154 if (!mret) return -2;
2155 if (*mret) return -2;
2157 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2178 /* ************************************************************************** */
2179 /* Stuff below this line is not for public consumption */
2180 /* ************************************************************************** */
2183 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2185 if (ipc->network_status_cb) ipc->network_status_cb(1);
2186 #ifdef THREADED_CLIENT
2187 pthread_mutex_lock(&(ipc->mutex));
2192 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2194 #ifdef THREADED_CLIENT
2195 pthread_mutex_unlock(&(ipc->mutex));
2197 if (ipc->network_status_cb) ipc->network_status_cb(0);
2201 /* Read a listing from the server up to 000. Append to dest if it exists */
2202 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2211 length = strlen(ret);
2216 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2217 linelength = strlen(aaa);
2218 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2220 strcpy(&ret[length], aaa);
2221 length += linelength;
2222 strcpy(&ret[length++], "\n");
2230 /* Send a listing to the server; generate the ending 000. */
2231 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2235 text = (char *)malloc(strlen(listing) + 6);
2237 strcpy(text, listing);
2238 while (text[strlen(text) - 1] == '\n')
2239 text[strlen(text) - 1] = '\0';
2240 strcat(text, "\n000");
2241 CtdlIPC_putline(ipc, text);
2245 /* Malloc failed but we are committed to send */
2246 /* This may result in extra blanks at the bottom */
2247 CtdlIPC_putline(ipc, text);
2248 CtdlIPC_putline(ipc, "000");
2254 /* Partial read of file from server */
2255 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2257 register size_t len = 0;
2261 if (!cret) return 0;
2262 if (bytes < 1) return 0;
2265 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2266 CtdlIPC_putline(ipc, aaa);
2267 CtdlIPC_getline(ipc, aaa);
2269 strcpy(cret, &aaa[4]);
2271 len = extract_long(&aaa[4], 0);
2272 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2274 /* I know what I'm doing */
2275 serv_read(ipc, ((char *)(*buf) + offset), len);
2277 /* We have to read regardless */
2278 serv_read(ipc, aaa, len);
2282 CtdlIPC_unlock(ipc);
2288 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2292 if (!cret) return -2;
2293 if (!ipc->downloading) return -2;
2295 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2297 ipc->downloading = 0;
2303 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2307 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2308 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2315 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2316 void (*progress_gauge_callback)
2317 (CtdlIPC*, unsigned long, unsigned long),
2320 register size_t len;
2322 if (!cret) return -1;
2323 if (!buf) return -1;
2324 if (*buf) return -1;
2325 if (!ipc->downloading) return -1;
2328 if (progress_gauge_callback)
2329 progress_gauge_callback(ipc, len, bytes);
2330 while (len < bytes) {
2331 register size_t block;
2333 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2339 if (progress_gauge_callback)
2340 progress_gauge_callback(ipc, len, bytes);
2345 /* READ - pipelined */
2346 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2348 void (*progress_gauge_callback)
2349 (CtdlIPC*, unsigned long, unsigned long),
2352 register size_t len;
2353 register int calls; /* How many calls in the pipeline */
2354 register int i; /* iterator */
2357 if (!cret) return -1;
2358 if (!buf) return -1;
2359 if (*buf) return -1;
2360 if (!ipc->downloading) return -1;
2362 *buf = (void *)realloc(*buf, bytes - resume);
2363 if (!*buf) return -1;
2367 if (progress_gauge_callback)
2368 progress_gauge_callback(ipc, len, bytes);
2370 /* How many calls will be in the pipeline? */
2371 calls = (bytes - resume) / 4096;
2372 if ((bytes - resume) % 4096) calls++;
2374 /* Send all requests at once */
2375 for (i = 0; i < calls; i++) {
2376 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2377 CtdlIPC_putline(ipc, aaa);
2380 /* Receive all responses at once */
2381 for (i = 0; i < calls; i++) {
2382 CtdlIPC_getline(ipc, aaa);
2384 strcpy(cret, &aaa[4]);
2386 len = extract_long(&aaa[4], 0);
2387 /* I know what I'm doing */
2388 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2390 if (progress_gauge_callback)
2391 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2393 CtdlIPC_unlock(ipc);
2399 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2404 if (!cret) return -1;
2405 if (!ipc->uploading) return -1;
2407 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2408 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2415 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2416 void (*progress_gauge_callback)
2417 (CtdlIPC*, unsigned long, unsigned long),
2420 register int ret = -1;
2421 register size_t offset = 0;
2425 FILE *fd = uploadFP;
2428 if (!cret) return -1;
2430 fseek(fd, 0L, SEEK_END);
2434 if (progress_gauge_callback)
2435 progress_gauge_callback(ipc, 0, bytes);
2437 while (offset < bytes) {
2438 register size_t to_write;
2440 /* Read some data in */
2441 to_write = fread(buf, 1, 4096, fd);
2443 if (feof(fd) || ferror(fd)) break;
2445 sprintf(aaa, "WRIT %d", (int)to_write);
2446 CtdlIPC_putline(ipc, aaa);
2447 CtdlIPC_getline(ipc, aaa);
2448 strcpy(cret, &aaa[4]);
2450 if (aaa[0] == '7') {
2451 to_write = extract_long(&aaa[4], 0);
2453 serv_write(ipc, buf, to_write);
2455 if (progress_gauge_callback)
2456 progress_gauge_callback(ipc, offset, bytes);
2457 /* Detect short reads and back up if needed */
2458 /* offset will never be negative anyway */
2459 fseek(fd, (signed)offset, SEEK_SET);
2464 if (progress_gauge_callback)
2465 progress_gauge_callback(ipc, 1, 1);
2468 return (!ferr ? ret : -2);
2473 * Generic command method. This method should handle any server command
2474 * except for CHAT. It takes the following arguments:
2476 * ipc The server to speak with
2477 * command Preformatted command to send to server
2478 * to_send A text or binary file to send to server
2479 * (only sent if server requests it)
2480 * bytes_to_send The number of bytes in to_send (required if
2481 * sending binary, optional if sending listing)
2482 * to_receive Pointer to a NULL pointer, if the server
2483 * sends text or binary we will allocate memory
2484 * for the file and stuff it here
2485 * bytes_to_receive If a file is received, we will store its
2487 * proto_response The protocol response. Caller must provide
2488 * this buffer and ensure that it is at least
2489 * 128 bytes in length.
2491 * This function returns a number equal to the protocol response number,
2492 * -1 if an internal error occurred, -2 if caller provided bad values,
2493 * or 0 - the protocol response number if bad values were found during
2494 * the protocol exchange.
2495 * It stores the protocol response string (minus the number) in
2496 * protocol_response as described above. Some commands send additional
2497 * data in this string.
2499 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2500 const char *command, const char *to_send,
2501 size_t bytes_to_send, char **to_receive,
2502 size_t *bytes_to_receive, char *proto_response)
2508 if (!command) return -2;
2509 if (!proto_response) return -2;
2512 if (ipc->ssl) watch_ssl = 1;
2516 CtdlIPC_putline(ipc, command);
2518 CtdlIPC_getline(ipc, proto_response);
2519 if (proto_response[3] == '*')
2521 ret = atoi(proto_response);
2522 strcpy(proto_response, &proto_response[4]);
2523 switch (ret / 100) {
2524 default: /* Unknown, punt */
2526 case 3: /* MORE_DATA */
2528 /* Don't need to do anything */
2530 case 1: /* LISTING_FOLLOWS */
2531 if (to_receive && !*to_receive && bytes_to_receive) {
2532 *to_receive = CtdlIPCReadListing(ipc, NULL);
2533 } else { /* Drain */
2534 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2538 case 4: /* SEND_LISTING */
2540 CtdlIPCSendListing(ipc, to_send);
2542 /* No listing given, fake it */
2543 CtdlIPC_putline(ipc, "000");
2547 case 6: /* BINARY_FOLLOWS */
2548 if (to_receive && !*to_receive && bytes_to_receive) {
2550 extract_long(proto_response, 0);
2551 *to_receive = (char *)
2552 malloc((size_t)*bytes_to_receive);
2556 serv_read(ipc, *to_receive,
2563 drain = extract_long(proto_response, 0);
2564 while (drain > SIZ) {
2565 serv_read(ipc, buf, SIZ);
2568 serv_read(ipc, buf, drain);
2572 case 7: /* SEND_BINARY */
2573 if (to_send && bytes_to_send) {
2574 serv_write(ipc, to_send, bytes_to_send);
2575 } else if (bytes_to_send) {
2576 /* Fake it, send nulls */
2579 fake = bytes_to_send;
2580 memset(buf, '\0', SIZ);
2581 while (fake > SIZ) {
2582 serv_write(ipc, buf, SIZ);
2585 serv_write(ipc, buf, fake);
2587 } /* else who knows? DANGER WILL ROBINSON */
2589 case 8: /* START_CHAT_MODE */
2590 if (!strncasecmp(command, "CHAT", 4)) {
2591 /* Don't call chatmode with generic! */
2592 CtdlIPC_putline(ipc, "/quit");
2595 /* In this mode we send then receive listing */
2597 CtdlIPCSendListing(ipc, to_send);
2599 /* No listing given, fake it */
2600 CtdlIPC_putline(ipc, "000");
2603 if (to_receive && !*to_receive
2604 && bytes_to_receive) {
2605 *to_receive = CtdlIPCReadListing(ipc, NULL);
2606 } else { /* Drain */
2607 while (CtdlIPC_getline(ipc, buf),
2608 strcmp(buf, "000")) ;
2613 case 9: /* ASYNC_MSG */
2614 /* CtdlIPCDoAsync(ret, proto_response); */
2615 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2621 CtdlIPC_unlock(ipc);
2626 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2628 struct hostent *phe;
2629 struct servent *pse;
2630 struct protoent *ppe;
2631 struct sockaddr_in sin;
2634 memset(&sin, 0, sizeof(sin));
2635 sin.sin_family = AF_INET;
2637 pse = getservbyname(service, protocol);
2639 sin.sin_port = pse->s_port;
2641 else if (atoi(service) > 0) {
2642 sin.sin_port = htons(atoi(service));
2645 sin.sin_port = htons(defaultPort);
2647 phe = gethostbyname(host);
2649 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2650 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2653 if ((ppe = getprotobyname(protocol)) == 0) {
2656 if (!strcmp(protocol, "udp")) {
2662 s = socket(PF_INET, type, ppe->p_proto);
2667 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2675 static int uds_connectsock(int *isLocal, char *sockpath)
2677 struct sockaddr_un addr;
2680 memset(&addr, 0, sizeof(addr));
2681 addr.sun_family = AF_UNIX;
2682 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2684 s = socket(AF_UNIX, SOCK_STREAM, 0);
2689 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2700 * input binary data from socket
2702 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2704 unsigned int len, rlen;
2706 #if defined(HAVE_OPENSSL)
2708 serv_read_ssl(ipc, buf, bytes);
2713 while (len < bytes) {
2714 rlen = read(ipc->sock, &buf[len], bytes - len);
2716 connection_died(ipc, 0);
2725 * send binary to server
2727 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2729 unsigned int bytes_written = 0;
2732 #if defined(HAVE_OPENSSL)
2734 serv_write_ssl(ipc, buf, nbytes);
2738 while (bytes_written < nbytes) {
2739 retval = write(ipc->sock, &buf[bytes_written],
2740 nbytes - bytes_written);
2742 connection_died(ipc, 0);
2745 bytes_written += retval;
2752 * input binary data from encrypted connection
2754 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2760 while (len < bytes) {
2761 if (SSL_want_read(ipc->ssl)) {
2762 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2763 error_printf("SSL_write in serv_read:\n");
2764 ERR_print_errors_fp(stderr);
2767 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2771 errval = SSL_get_error(ipc->ssl, rlen);
2772 if (errval == SSL_ERROR_WANT_READ ||
2773 errval == SSL_ERROR_WANT_WRITE) {
2778 Not sure why we'd want to handle these error codes any differently,
2779 but this definitely isn't the way to handle them. Someone must have
2780 naively assumed that we could fall back to unencrypted communications,
2781 but all it does is just recursively blow the stack.
2782 if (errval == SSL_ERROR_ZERO_RETURN ||
2783 errval == SSL_ERROR_SSL) {
2784 serv_read(ipc, &buf[len], bytes - len);
2788 error_printf("SSL_read in serv_read: %s\n",
2789 ERR_reason_error_string(ERR_peek_error()));
2790 connection_died(ipc, 1);
2799 * send binary to server encrypted
2801 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2803 unsigned int bytes_written = 0;
2807 while (bytes_written < nbytes) {
2808 if (SSL_want_write(ipc->ssl)) {
2809 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2810 error_printf("SSL_read in serv_write:\n");
2811 ERR_print_errors_fp(stderr);
2814 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2815 nbytes - bytes_written);
2819 errval = SSL_get_error(ipc->ssl, retval);
2820 if (errval == SSL_ERROR_WANT_READ ||
2821 errval == SSL_ERROR_WANT_WRITE) {
2825 if (errval == SSL_ERROR_ZERO_RETURN ||
2826 errval == SSL_ERROR_SSL) {
2827 serv_write(ipc, &buf[bytes_written],
2828 nbytes - bytes_written);
2831 error_printf("SSL_write in serv_write: %s\n",
2832 ERR_reason_error_string(ERR_peek_error()));
2833 connection_died(ipc, 1);
2836 bytes_written += retval;
2841 #ifdef THREADED_CLIENT
2842 static void ssl_lock(int mode, int n, const char *file, int line)
2844 if (mode & CRYPTO_LOCK)
2845 pthread_mutex_lock(Critters[n]);
2847 pthread_mutex_unlock(Critters[n]);
2849 #endif /* THREADED_CLIENT */
2852 static void CtdlIPC_init_OpenSSL(void)
2855 SSL_METHOD *ssl_method;
2858 /* already done init */
2867 SSL_load_error_strings();
2868 SSLeay_add_ssl_algorithms();
2870 /* Set up the SSL context in which we will oeprate */
2871 ssl_method = SSLv23_client_method();
2872 ssl_ctx = SSL_CTX_new(ssl_method);
2874 error_printf("SSL_CTX_new failed: %s\n",
2875 ERR_reason_error_string(ERR_get_error()));
2878 /* Any reasonable cipher we can get */
2879 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2880 error_printf("No ciphers available for encryption\n");
2883 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2885 /* Load DH parameters into the context */
2888 error_printf("Can't allocate a DH object: %s\n",
2889 ERR_reason_error_string(ERR_get_error()));
2892 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2893 error_printf("Can't assign DH_P: %s\n",
2894 ERR_reason_error_string(ERR_get_error()));
2898 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2899 error_printf("Can't assign DH_G: %s\n",
2900 ERR_reason_error_string(ERR_get_error()));
2905 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2908 #ifdef THREADED_CLIENT
2909 /* OpenSSL requires callbacks for threaded clients */
2910 CRYPTO_set_locking_callback(ssl_lock);
2911 CRYPTO_set_id_callback(id_callback);
2913 /* OpenSSL requires us to do semaphores for threaded clients */
2914 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2916 perror("malloc failed");
2919 for (a = 0; a < CRYPTO_num_locks(); a++) {
2920 Critters[a] = malloc(sizeof (pthread_mutex_t));
2922 perror("malloc failed");
2925 pthread_mutex_init(Critters[a], NULL);
2928 #endif /* THREADED_CLIENT */
2933 #ifdef THREADED_CLIENT
2934 static unsigned long id_callback(void) {
2935 return (unsigned long)pthread_self();
2937 #endif /* THREADED_CLIENT */
2938 #endif /* HAVE_OPENSSL */
2942 ReadNetworkChunk(CtdlIPC* ipc)
2959 FD_SET(ipc->sock, &read_fd);
2960 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2962 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2966 *(ipc->BufPtr) = '\0';
2967 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2968 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2970 ipc->BufPtr[n]='\0';
2978 if (!(errno == EINTR || errno == EAGAIN))
2979 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2985 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2987 ipc->BufPtr[n]='\0';
2992 connection_died(ipc, 0);
3000 * input string from socket - implemented in terms of serv_read()
3004 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3007 char *aptr, *bptr, *aeptr, *beptr;
3009 // error_printf("---\n");
3012 #if defined(HAVE_OPENSSL)
3015 /* Read one character at a time. */
3017 serv_read(ipc, &buf[i], 1);
3018 if (buf[i] == '\n' || i == (SIZ-1))
3022 /* If we got a long line, discard characters until the newline. */
3024 while (buf[i] != '\n')
3025 serv_read(ipc, &buf[i], 1);
3027 /* Strip the trailing newline (and carriage return, if present) */
3028 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3029 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3034 if (ipc->Buf == NULL)
3037 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3039 ipc->BufPtr = ipc->Buf;
3043 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3044 if (ipc->BufUsed == 0)
3045 ReadNetworkChunk(ipc);
3047 //// if (ipc->BufUsed != 0) while (1)
3053 aeptr = ipc->Buf + ipc->BufSize;
3054 while ((aptr < aeptr) &&
3058 *(bptr++) = *(aptr++);
3059 if ((*aptr == '\n') && (aptr < aeptr))
3061 /* Terminate it right, remove the line breaks */
3062 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3064 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3067 // 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);
3068 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3071 /* is there more in the buffer we need to read later? */
3072 if (ipc->Buf + ipc->BufUsed > aptr)
3079 ipc->BufPtr = ipc->Buf;
3081 // error_printf("----bla6\n");
3084 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3085 else if ((ipc->BufPtr != ipc->Buf) &&
3086 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3088 size_t NewBufSize = ipc->BufSize * 2;
3089 int delta = (ipc->BufPtr - ipc->Buf);
3092 /* if the line would end after our buffer, we should use a bigger buffer. */
3093 NewBuf = (char *)malloc (NewBufSize + 10);
3094 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3096 ipc->Buf = ipc->BufPtr = NewBuf;
3097 ipc->BufUsed -= delta;
3098 ipc->BufSize = NewBufSize;
3100 if (ReadNetworkChunk(ipc) <0)
3102 // error_printf("----bla\n");
3106 /// error_printf("----bl45761%s\nipc->BufUsed");
3108 // error_printf("----bla1\n");
3111 #else /* CHUNKED_READ */
3113 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3117 /* Read one character at a time. */
3119 serv_read(ipc, &buf[i], 1);
3120 if (buf[i] == '\n' || i == (SIZ-1))
3124 /* If we got a long line, discard characters until the newline. */
3126 while (buf[i] != '\n')
3127 serv_read(ipc, &buf[i], 1);
3129 /* Strip the trailing newline (and carriage return, if present) */
3130 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3131 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3135 #endif /* CHUNKED_READ */
3138 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3140 CtdlIPC_getline(ipc, buf);
3144 * send line to server - implemented in terms of serv_write()
3146 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3152 cmd = malloc(len + 2);
3154 /* This requires no extra memory */
3155 serv_write(ipc, buf, len);
3156 serv_write(ipc, "\n", 1);
3158 /* This is network-optimized */
3159 strncpy(cmd, buf, len);
3160 strcpy(cmd + len, "\n");
3161 serv_write(ipc, cmd, len + 1);
3165 ipc->last_command_sent = time(NULL);
3168 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3170 CtdlIPC_putline(ipc, buf);
3177 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3185 ipc = ialloc(CtdlIPC);
3189 #if defined(HAVE_OPENSSL)
3191 CtdlIPC_init_OpenSSL();
3193 #if defined(HAVE_PTHREAD_H)
3194 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3196 ipc->sock = -1; /* Not connected */
3197 ipc->isLocal = 0; /* Not local, of course! */
3198 ipc->downloading = 0;
3200 ipc->last_command_sent = 0L;
3201 ipc->network_status_cb = NULL;
3206 strcpy(cithost, DEFAULT_HOST); /* default host */
3207 strcpy(citport, DEFAULT_PORT); /* default port */
3209 /* Allow caller to supply our values (Windows) */
3210 if (hostbuf && strlen(hostbuf) > 0)
3211 strcpy(cithost, hostbuf);
3212 if (portbuf && strlen(portbuf) > 0)
3213 strcpy(citport, portbuf);
3215 /* Read host/port from command line if present */
3216 for (a = 0; a < argc; ++a) {
3219 } else if (a == 1) {
3220 strcpy(cithost, argv[a]);
3221 } else if (a == 2) {
3222 strcpy(citport, argv[a]);
3224 error_printf("%s: usage: ",argv[0]);
3225 error_printf("%s [host] [port] ",argv[0]);
3232 if ((!strcmp(cithost, "localhost"))
3233 || (!strcmp(cithost, "127.0.0.1"))) {
3237 /* If we're using a unix domain socket we can do a bunch of stuff */
3238 if (!strcmp(cithost, UDS)) {
3239 if (!strcasecmp(citport, DEFAULT_PORT)) {
3240 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3243 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3245 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3246 if (ipc->sock == -1) {
3250 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3251 if (portbuf != NULL) strcpy(portbuf, sockpath);
3255 ipc->sock = connectsock(cithost, citport, "tcp", 504);
3256 if (ipc->sock == -1) {
3260 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3261 if (portbuf != NULL) strcpy(portbuf, citport);
3267 * Disconnect and delete the IPC class (destructor)
3269 void CtdlIPC_delete(CtdlIPC* ipc)
3273 SSL_shutdown(ipc->ssl);
3278 if (ipc->sock > -1) {
3279 shutdown(ipc->sock, 2); /* Close it up */
3282 if (ipc->Buf != NULL)
3291 * Disconnect and delete the IPC class (destructor)
3292 * Also NULLs out the pointer
3294 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3296 CtdlIPC_delete(*pipc);
3302 * return the file descriptor of the server socket so we can select() on it.
3304 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3307 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3314 * return one character
3316 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3319 char CtdlIPC_get(CtdlIPC* ipc)
3324 serv_read(ipc, buf, 1);