16 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <netinet/in.h>
29 #ifdef THREADED_CLIENT
32 #include <libcitadel.h>
34 #include "citadel_ipc.h"
35 #include "citadel_decls.h"
36 #include "citadel_dirs.h"
37 #ifdef THREADED_CLIENT
38 pthread_mutex_t rwlock;
42 static SSL_CTX *ssl_ctx;
45 #ifdef THREADED_CLIENT
46 pthread_mutex_t **Critters; /* Things that need locking */
47 #endif /* THREADED_CLIENT */
49 #endif /* HAVE_OPENSSL */
52 #define INADDR_NONE 0xffffffff
55 static void (*status_hook)(char *s) = NULL;
57 void setCryptoStatusHook(void (*hook)(char *s)) {
61 void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
62 ipc->network_status_cb = hook;
66 char instant_msgs = 0;
69 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
70 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
72 static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
73 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
74 static void endtls(SSL *ssl);
75 #ifdef THREADED_CLIENT
76 static unsigned long id_callback(void);
77 #endif /* THREADED_CLIENT */
78 #endif /* HAVE_OPENSSL */
79 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
80 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
84 * Does nothing. The server should always return 200.
86 int CtdlIPCNoop(CtdlIPC *ipc)
90 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
95 * Does nothing interesting. The server should always return 200
96 * along with your string.
98 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
104 if (!cret) return -2;
106 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
109 sprintf(aaa, "ECHO %s", arg);
110 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
117 * Asks the server to close the connecction.
118 * Should always return 200.
120 int CtdlIPCQuit(CtdlIPC *ipc)
122 register int ret = 221; /* Default to successful quit */
126 if (ipc->sock > -1) {
127 CtdlIPC_putline(ipc, "QUIT");
128 CtdlIPC_getline(ipc, aaa);
133 SSL_shutdown(ipc->ssl);
137 shutdown(ipc->sock, 2); /* Close connection; we're dead */
145 * Asks the server to logout. Should always return 200, even if no user
146 * was logged in. The user will not be logged in after this!
148 int CtdlIPCLogout(CtdlIPC *ipc)
154 CtdlIPC_putline(ipc, "LOUT");
155 CtdlIPC_getline(ipc, aaa);
163 * First stage of authentication - pass the username. Returns 300 if the
164 * username is able to log in, with the username correctly spelled in cret.
165 * Returns various 500 error codes if the user doesn't exist, etc.
167 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
172 if (!username) return -2;
173 if (!cret) return -2;
175 aaa = (char *)malloc((size_t)(strlen(username) + 6));
178 sprintf(aaa, "USER %s", username);
179 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
186 * Second stage of authentication - provide password. The server returns
187 * 200 and several arguments in cret relating to the user's account.
189 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
194 if (!passwd) return -2;
195 if (!cret) return -2;
197 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
200 sprintf(aaa, "PASS %s", passwd);
201 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
208 * Second stage of authentication - provide password. The server returns
209 * 200 and several arguments in cret relating to the user's account.
211 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
216 if (!response) return -2;
217 if (!cret) return -2;
219 aaa = (char *)malloc((size_t)(strlen(response) + 6));
222 sprintf(aaa, "PAS2 %s", response);
223 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
230 * Create a new user. This returns 200 plus the same arguments as TryPassword
231 * if selfservice is nonzero, unless there was a problem creating the account.
232 * If selfservice is zero, creates a new user but does not log out the existing
233 * user - intended for use by system administrators to create accounts on
234 * behalf of other users.
236 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
241 if (!username) return -2;
242 if (!cret) return -2;
244 aaa = (char *)malloc((size_t)(strlen(username) + 6));
247 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
248 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
255 * Changes the user's password. Returns 200 if changed, errors otherwise.
257 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
262 if (!passwd) return -2;
263 if (!cret) return -2;
265 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
268 sprintf(aaa, "SETP %s", passwd);
269 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
276 /* Caller must free the march list */
277 /* Room types are defined in enum RoomList; keep these in sync! */
278 /* floor is -1 for all, or floornum */
279 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
282 struct march *march = NULL;
283 static char *proto[] =
284 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
289 if (!listing) return -2;
290 if (*listing) return -2; /* Free the listing first */
291 if (!cret) return -2;
292 /* if (which < 0 || which > 4) return -2; */
293 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
295 sprintf(aaa, "%s %d", proto[which], floor);
296 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
297 if (ret / 100 == 1) {
300 while (bbb && strlen(bbb)) {
303 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
305 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
306 mptr = (struct march *) malloc(sizeof (struct march));
309 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
310 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
311 mptr->march_floor = (char) extract_int(aaa, 2);
312 mptr->march_order = (char) extract_int(aaa, 3);
313 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
314 mptr->march_access = (char) extract_int(aaa, 5);
321 while (mptr2->next != NULL)
335 /* Caller must free the struct ctdluser; caller may pass an existing one */
336 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
340 if (!cret) return -2;
341 if (!uret) return -2;
342 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
343 if (!*uret) return -1;
345 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
346 if (ret / 100 == 2) {
347 uret[0]->USscreenwidth = extract_int(cret, 0);
348 uret[0]->USscreenheight = extract_int(cret, 1);
349 uret[0]->flags = extract_int(cret, 2);
356 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
360 if (!uret) return -2;
361 if (!cret) return -2;
363 sprintf(aaa, "SETU %d|%d|%d",
364 uret->USscreenwidth, uret->USscreenheight,
366 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
371 int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
376 if (!oldname) return -2;
377 if (!newname) return -2;
378 if (!cret) return -2;
380 snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
381 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
387 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
388 struct ctdlipcroom **rret, char *cret)
393 if (!cret) return -2;
394 if (!rret) return -2;
395 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
396 if (!*rret) return -1;
399 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
404 sprintf(aaa, "GOTO %s|%s", room, passwd);
406 aaa = (char *)malloc(strlen(room) + 6);
411 sprintf(aaa, "GOTO %s", room);
413 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
414 if (ret / 100 == 2) {
415 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
416 rret[0]->RRunread = extract_long(cret, 1);
417 rret[0]->RRtotal = extract_long(cret, 2);
418 rret[0]->RRinfoupdated = extract_int(cret, 3);
419 rret[0]->RRflags = extract_int(cret, 4);
420 rret[0]->RRhighest = extract_long(cret, 5);
421 rret[0]->RRlastread = extract_long(cret, 6);
422 rret[0]->RRismailbox = extract_int(cret, 7);
423 rret[0]->RRaide = extract_int(cret, 8);
424 rret[0]->RRnewmail = extract_long(cret, 9);
425 rret[0]->RRfloor = extract_int(cret, 10);
426 rret[0]->RRflags2 = extract_int(cret, 14);
437 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
438 /* whicharg is number of messages, applies to last, first, gt, lt */
439 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
440 const char *mtemplate, unsigned long **mret, char *cret)
443 register unsigned long count = 0;
444 static char *proto[] =
445 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
450 if (!cret) return -2;
451 if (!mret) return -2;
452 if (*mret) return -2;
453 if (which < 0 || which > 6) return -2;
456 sprintf(aaa, "MSGS %s||%d", proto[which],
457 (mtemplate) ? 1 : 0);
459 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
460 (mtemplate) ? 1 : 0);
461 if (mtemplate) count = strlen(mtemplate);
462 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
466 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
469 while (bbb && strlen(bbb)) {
470 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
471 remove_token(bbb, 0, '\n');
472 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
473 sizeof (unsigned long)));
475 (*mret)[count++] = atol(aaa);
487 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
488 struct ctdlipcmessage **mret, char *cret)
494 int multipart_hunting = 0;
495 char multipart_prefix[128];
498 if (!cret) return -1;
499 if (!mret) return -1;
500 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
501 if (!*mret) return -1;
502 if (!msgnum) return -1;
504 strcpy(encoding, "");
505 strcpy(mret[0]->content_type, "");
506 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
507 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
508 if (ret / 100 == 1) {
510 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
511 while (strlen(bbb) > 4 && bbb[4] == '=') {
512 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
513 remove_token(bbb, 0, '\n');
515 if (!strncasecmp(aaa, "nhdr=yes", 8))
517 else if (!strncasecmp(aaa, "from=", 5))
518 safestrncpy(mret[0]->author, &aaa[5], SIZ);
519 else if (!strncasecmp(aaa, "type=", 5))
520 mret[0]->type = atoi(&aaa[5]);
521 else if (!strncasecmp(aaa, "msgn=", 5))
522 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
523 else if (!strncasecmp(aaa, "subj=", 5))
524 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
525 else if (!strncasecmp(aaa, "rfca=", 5))
526 safestrncpy(mret[0]->email, &aaa[5], SIZ);
527 else if (!strncasecmp(aaa, "hnod=", 5))
528 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
529 else if (!strncasecmp(aaa, "room=", 5))
530 safestrncpy(mret[0]->room, &aaa[5], SIZ);
531 else if (!strncasecmp(aaa, "node=", 5))
532 safestrncpy(mret[0]->node, &aaa[5], SIZ);
533 else if (!strncasecmp(aaa, "rcpt=", 5))
534 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
535 else if (!strncasecmp(aaa, "time=", 5))
536 mret[0]->time = atol(&aaa[5]);
538 /* Multipart/alternative prefix & suffix strings help
539 * us to determine which part we want to download.
541 else if (!strncasecmp(aaa, "pref=", 5)) {
542 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
543 if (!strcasecmp(multipart_prefix,
544 "multipart/alternative")) {
548 else if (!strncasecmp(aaa, "suff=", 5)) {
549 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
550 if (!strcasecmp(multipart_prefix,
551 "multipart/alternative")) {
556 else if (!strncasecmp(aaa, "part=", 5)) {
557 struct parts *ptr, *chain;
559 ptr = (struct parts *)calloc(1, sizeof (struct parts));
562 /* Fill the buffers for the caller */
563 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
564 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
565 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
566 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
567 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
568 ptr->length = extract_long(&aaa[5], 5);
569 if (!mret[0]->attachments)
570 mret[0]->attachments = ptr;
572 chain = mret[0]->attachments;
578 /* Now handle multipart/alternative */
579 if (multipart_hunting > 0) {
580 if ( (!strcasecmp(ptr->mimetype,
582 || (!strcasecmp(ptr->mimetype,
584 strcpy(mret[0]->mime_chosen,
592 /* Eliminate "text\n" */
593 remove_token(bbb, 0, '\n');
595 /* If doing a MIME thing, pull out the extra headers */
598 if (!strncasecmp(bbb, "Content-type:", 13)) {
599 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
600 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
601 striplt(mret[0]->content_type);
603 /* strip out ";charset=" portion. FIXME do something with
604 * the charset (like... convert it) instead of just throwing
607 if (strstr(mret[0]->content_type, ";") != NULL) {
608 strcpy(strstr(mret[0]->content_type, ";"), "");
612 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
613 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
614 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
615 striplt(mret[0]->mime_chosen);
617 if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
618 extract_token(encoding, bbb, 0, '\n', sizeof encoding);
619 strcpy(encoding, &encoding[26]);
622 remove_token(bbb, 0, '\n');
623 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
624 remove_token(bbb, 0, '\n');
631 if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
633 int bytes_decoded = 0;
634 ccc = malloc(strlen(bbb) + 32768);
635 if (!strcasecmp(encoding, "base64")) {
636 bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
638 else if (!strcasecmp(encoding, "quoted-printable")) {
639 bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
641 ccc[bytes_decoded] = 0;
646 /* FIXME: Strip trailing whitespace */
647 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
650 bbb = (char *)realloc(bbb, 1);
660 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
665 if (!cret) return -2;
666 if (!listing) return -2;
667 if (*listing) return -2;
669 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
675 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
679 char *listing = NULL;
682 if (!cret) return -2;
684 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
685 if (ret / 100 == 1) {
688 while (*listing && strlen(listing)) {
689 extract_token(buf, listing, 0, '\n', sizeof buf);
690 remove_token(listing, 0, '\n');
692 case 0: ipc->ServInfo.pid = atoi(buf);
694 case 1: strcpy(ipc->ServInfo.nodename,buf);
696 case 2: strcpy(ipc->ServInfo.humannode,buf);
698 case 3: strcpy(ipc->ServInfo.fqdn,buf);
700 case 4: strcpy(ipc->ServInfo.software,buf);
702 case 5: ipc->ServInfo.rev_level = atoi(buf);
704 case 6: strcpy(ipc->ServInfo.site_location,buf);
706 case 7: strcpy(ipc->ServInfo.sysadm,buf);
708 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
710 case 10: ipc->ServInfo.ok_floors = atoi(buf);
712 case 11: ipc->ServInfo.paging_level = atoi(buf);
714 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
716 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
718 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
724 if (listing) free(listing);
730 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
735 if (!cret) return -2;
736 if (!listing) return -2;
737 if (*listing) return -2;
739 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
745 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
747 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
752 if (!cret) return -2;
755 sprintf(aaa, "SLRP %ld", msgnum);
758 sprintf(aaa, "SLRP HIGHEST");
760 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
766 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
771 if (!cret) return -2;
772 if (!username) return -2;
774 aaa = (char *)malloc(strlen(username) + 6);
777 sprintf(aaa, "INVT %s", username);
778 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
785 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
790 if (!cret) return -1;
791 if (!username) return -1;
793 aaa = (char *)malloc(strlen(username) + 6);
795 sprintf(aaa, "KICK %s", username);
796 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
803 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
807 if (!cret) return -2;
808 if (!qret) return -2;
809 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
810 if (!*qret) return -1;
812 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
813 if (ret / 100 == 2) {
814 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
815 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
816 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
817 qret[0]->QRflags = extract_int(cret, 3);
818 qret[0]->QRfloor = extract_int(cret, 4);
819 qret[0]->QRorder = extract_int(cret, 5);
820 qret[0]->QRdefaultview = extract_int(cret, 6);
821 qret[0]->QRflags2 = extract_int(cret, 7);
828 /* set forget to kick all users out of room */
829 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
834 if (!cret) return -2;
835 if (!qret) return -2;
837 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
838 strlen(qret->QRdirname) + 64);
841 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
842 qret->QRname, qret->QRpasswd, qret->QRdirname,
843 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
844 qret->QRdefaultview, qret->QRflags2);
845 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
852 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
854 if (!cret) return -1;
856 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
861 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
866 if (!cret) return -2;
867 if (!username) return -2;
869 aaa = (char *)malloc(strlen(username) + 6);
872 sprintf(aaa, "SETA %s", username);
873 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
880 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, const struct ctdlipcmessage *mr, char *cret)
885 if (!cret) return -2;
888 snprintf(cmd, sizeof cmd,
889 "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
890 mr->anonymous, mr->type, mr->subject, mr->author);
891 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
893 if ((flag == 0) && (subject_required != NULL)) {
894 /* Is the server strongly recommending that the user enter a message subject? */
895 if ((cret[3] != '\0') && (cret[4] != '\0')) {
896 *subject_required = extract_int(&cret[4], 1);
906 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
910 if (!cret) return -2;
911 if (!iret) return -2;
912 if (*iret) return -2;
914 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
919 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
923 if (!cret) return -2;
924 if (!msgnum) return -2;
926 sprintf(aaa, "DELE %ld", msgnum);
927 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
932 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
937 if (!cret) return -2;
938 if (!destroom) return -2;
939 if (!msgnum) return -2;
941 aaa = (char *)malloc(strlen(destroom) + 28);
944 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
945 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
952 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
956 if (!cret) return -2;
958 sprintf(aaa, "KILL %d", for_real);
959 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
964 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
965 const char *password, int floor, char *cret)
970 if (!cret) return -2;
971 if (!roomname) return -2;
974 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
976 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
979 aaa = (char *)malloc(strlen(roomname) + 40);
981 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
984 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
991 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
993 if (!cret) return -2;
995 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
1000 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
1006 if (!cret) return -2;
1007 if (!mret) return -2;
1008 if (*mret) return -2;
1009 if (!message) return -2;
1011 aaa = (char *)malloc(strlen(message) + 6);
1012 if (!aaa) return -1;
1014 sprintf(aaa, "MESG %s", message);
1015 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
1022 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
1024 if (!cret) return -2;
1026 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
1031 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
1037 if (!cret) return -2;
1038 if (!rret) return -2;
1039 if (*rret) return -2;
1042 aaa = (char *)malloc(strlen(username) + 6);
1044 aaa = (char *)malloc(12);
1045 if (!aaa) return -1;
1048 sprintf(aaa, "GREG %s", username);
1050 sprintf(aaa, "GREG _SELF_");
1051 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1058 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1063 if (!cret) return -2;
1064 if (!username) return -2;
1065 if (axlevel < 0 || axlevel > 7) return -2;
1067 aaa = (char *)malloc(strlen(username) + 17);
1068 if (!aaa) return -1;
1070 sprintf(aaa, "VALI %s|%d", username, axlevel);
1071 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1078 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1082 if (!cret) return -1;
1083 if (!info) return -1;
1085 sprintf(aaa, "EINF %d", for_real);
1086 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1091 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1097 if (!cret) return -1;
1098 if (!listing) return -1;
1099 if (*listing) return -1;
1100 if (!searchstring) return -1;
1102 cmd = malloc(strlen(searchstring) + 10);
1103 sprintf(cmd, "LIST %s", searchstring);
1105 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1112 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1114 if (!cret) return -1;
1115 if (!info) return -1;
1117 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1123 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1127 if (!cret) return -1;
1128 if (!chek) return -1;
1130 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1131 if (ret / 100 == 2) {
1132 chek->newmail = extract_long(cret, 0);
1133 chek->needregis = extract_int(cret, 1);
1134 chek->needvalid = extract_int(cret, 2);
1141 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1146 if (!cret) return -2;
1147 if (!filename) return -2;
1149 aaa = (char *)malloc(strlen(filename) + 6);
1150 if (!aaa) return -1;
1152 sprintf(aaa, "DELF %s", filename);
1153 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1160 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1165 if (!cret) return -2;
1166 if (!filename) return -2;
1167 if (!destroom) return -2;
1169 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1170 if (!aaa) return -1;
1172 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1173 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1180 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1185 if (!cret) return -2;
1186 if (!filename) return -2;
1187 if (!destnode) return -2;
1189 aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1190 if (!aaa) return -1;
1192 sprintf(aaa, "NETF %s|%s", filename, destnode);
1193 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1200 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1205 if (!cret) return -1;
1206 if (!listing) return -1;
1207 if (*listing) return -1;
1209 *stamp = CtdlIPCServerTime(ipc, cret);
1211 *stamp = time(NULL);
1212 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1218 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1220 void (*progress_gauge_callback)
1221 (CtdlIPC*, unsigned long, unsigned long),
1230 if (!cret) return -2;
1231 if (!filename) return -2;
1232 if (!buf) return -2;
1233 if (*buf) return -2;
1234 if (ipc->downloading) return -2;
1236 aaa = (char *)malloc(strlen(filename) + 6);
1237 if (!aaa) return -1;
1239 sprintf(aaa, "OPEN %s", filename);
1240 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1242 if (ret / 100 == 2) {
1243 ipc->downloading = 1;
1244 bytes = extract_long(cret, 0);
1245 last_mod = extract_int(cret, 1);
1246 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1248 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1249 progress_gauge_callback, cret);
1251 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1252 progress_gauge_callback, cret);
1255 ret = CtdlIPCEndDownload(ipc, cret);
1257 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1258 filename, mimetype);
1265 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1267 void (*progress_gauge_callback)
1268 (CtdlIPC*, unsigned long, unsigned long),
1278 if (!cret) return -2;
1279 if (!buf) return -2;
1280 if (*buf) return -2;
1281 if (!part) return -2;
1282 if (!msgnum) return -2;
1283 if (ipc->downloading) return -2;
1285 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1286 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1287 if (ret / 100 == 2) {
1288 ipc->downloading = 1;
1289 bytes = extract_long(cret, 0);
1290 last_mod = extract_int(cret, 1);
1291 extract_token(filename, cret, 2, '|', sizeof filename);
1292 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1293 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1294 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1295 ret = CtdlIPCEndDownload(ipc, cret);
1297 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1298 filename, mimetype);
1305 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1306 void (*progress_gauge_callback)
1307 (CtdlIPC*, unsigned long, unsigned long),
1316 if (!cret) return -1;
1317 if (!buf) return -1;
1318 if (*buf) return -1;
1319 if (!filename) return -1;
1320 if (ipc->downloading) return -1;
1322 aaa = (char *)malloc(strlen(filename) + 6);
1323 if (!aaa) return -1;
1325 sprintf(aaa, "OIMG %s", filename);
1326 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1328 if (ret / 100 == 2) {
1329 ipc->downloading = 1;
1330 bytes = extract_long(cret, 0);
1331 last_mod = extract_int(cret, 1);
1332 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1333 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1334 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1335 ret = CtdlIPCEndDownload(ipc, cret);
1337 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1338 filename, mimetype);
1345 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1347 void (*progress_gauge_callback)
1348 (CtdlIPC*, unsigned long, unsigned long),
1354 char MimeTestBuf[64];
1355 const char *MimeType;
1358 if (!cret) return -1;
1359 if (!save_as) return -1;
1360 if (!comment) return -1;
1361 if (!path) return -1;
1362 if (!*path) return -1;
1363 if (ipc->uploading) return -1;
1365 uploadFP = fopen(path, "r");
1366 if (!uploadFP) return -2;
1368 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1373 MimeType = GuessMimeType(&MimeTestBuf[0], len);
1374 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1375 if (!aaa) return -1;
1377 sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType, comment);
1378 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1380 if (ret / 100 == 2) {
1382 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1383 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1391 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1392 const char *save_as,
1393 void (*progress_gauge_callback)
1394 (CtdlIPC*, unsigned long, unsigned long),
1400 char MimeTestBuf[64];
1401 const char *MimeType;
1404 if (!cret) return -1;
1405 if (!save_as) return -1;
1406 if (!path && for_real) return -1;
1407 if (!*path && for_real) return -1;
1408 if (ipc->uploading) return -1;
1410 aaa = (char *)malloc(strlen(save_as) + 17);
1411 if (!aaa) return -1;
1413 uploadFP = fopen(path, "r");
1414 if (!uploadFP) return -2;
1416 len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
1420 MimeType = GuessMimeType(&MimeTestBuf[0], 64);
1422 sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
1423 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1425 if (ret / 100 == 2 && for_real) {
1427 ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
1428 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1436 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1441 if (!cret) return -2;
1442 if (!username) return -2;
1444 aaa = (char *)malloc(strlen(username) + 6);
1445 if (!aaa) return -1;
1447 sprintf(aaa, "QUSR %s", username);
1448 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1455 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1459 if (!cret) return -2;
1460 if (!listing) return -2;
1461 if (*listing) return -2;
1463 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1468 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1473 if (!cret) return -2;
1474 if (!name) return -2;
1476 sprintf(aaa, "CFLR %s|%d", name, for_real);
1477 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1483 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1487 if (!cret) return -1;
1488 if (floornum < 0) return -1;
1490 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1491 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1496 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1501 if (!cret) return -2;
1502 if (!floorname) return -2;
1503 if (floornum < 0) return -2;
1505 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1506 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1514 * You only need to fill out hostname, the defaults will be used if any of the
1515 * other fields are not set properly.
1517 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1518 int revision, const char *software_name, const char *hostname,
1524 if (developerid < 0 || clientid < 0 || revision < 0 ||
1528 revision = REV_LEVEL - 600;
1529 software_name = "Citadel (libcitadel)";
1531 if (!hostname) return -2;
1533 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1534 if (!aaa) return -1;
1536 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1537 revision, software_name, hostname);
1538 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1545 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1551 if (!cret) return -2;
1552 if (!username) return -2;
1554 aaa = (char *)malloc(strlen(username) + 8);
1555 if (!aaa) return -1;
1558 sprintf(aaa, "SEXP %s|-", username);
1559 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1562 sprintf(aaa, "SEXP %s||", username);
1563 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1571 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1575 if (!cret) return -2;
1576 if (!listing) return -2;
1577 if (*listing) return -2;
1579 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1584 /* mode is 0 = enable, 1 = disable, 2 = status */
1585 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1589 if (!cret) return -2;
1591 sprintf(aaa, "DEXP %d", mode);
1592 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1597 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1599 if (!cret) return -2;
1600 if (!bio) return -2;
1602 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1608 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1614 if (!cret) return -2;
1615 if (!username) return -2;
1616 if (!listing) return -2;
1617 if (*listing) return -2;
1619 aaa = (char *)malloc(strlen(username) + 6);
1620 if (!aaa) return -1;
1622 sprintf(aaa, "RBIO %s", username);
1623 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1630 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1634 if (!cret) return -2;
1635 if (!listing) return -2;
1636 if (*listing) return -2;
1638 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1643 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1647 if (!cret) return -1;
1649 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1650 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1655 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1659 if (!cret) return -1;
1661 sprintf(aaa, "TERM %d", sid);
1662 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1667 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1669 if (!cret) return -1;
1671 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1676 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1680 if (!cret) return -1;
1682 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1683 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1688 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1694 if (!cret) return -2;
1695 if (!text) return -2;
1696 if (!filename) return -2;
1698 aaa = (char *)malloc(strlen(filename) + 6);
1699 if (!aaa) return -1;
1701 sprintf(aaa, "EMSG %s", filename);
1702 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1709 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1714 if (!cret) return -2;
1715 if (!hostname) return -2;
1717 aaa = (char *)malloc(strlen(hostname) + 6);
1718 if (!aaa) return -1;
1720 sprintf(aaa, "HCHG %s", hostname);
1721 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1728 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1733 if (!cret) return -2;
1734 if (!roomname) return -2;
1736 aaa = (char *)malloc(strlen(roomname) + 6);
1737 if (!aaa) return -1;
1739 sprintf(aaa, "RCHG %s", roomname);
1740 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1747 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1752 if (!cret) return -2;
1753 if (!username) return -2;
1755 aaa = (char *)malloc(strlen(username) + 6);
1756 if (!aaa) return -1;
1758 sprintf(aaa, "UCHG %s", username);
1759 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1766 /* This function returns the actual server time reported, or 0 if error */
1767 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1769 register time_t tret;
1772 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1773 if (ret / 100 == 2) {
1774 tret = extract_long(cret, 0);
1783 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1784 struct ctdluser **uret, char *cret)
1789 if (!cret) return -2;
1790 if (!uret) return -2;
1791 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1792 if (!*uret) return -1;
1794 sprintf(aaa, "AGUP %s", who);
1795 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1797 if (ret / 100 == 2) {
1798 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1799 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1800 uret[0]->flags = extract_int(cret, 2);
1801 uret[0]->timescalled = extract_long(cret, 3);
1802 uret[0]->posted = extract_long(cret, 4);
1803 uret[0]->axlevel = extract_int(cret, 5);
1804 uret[0]->usernum = extract_long(cret, 6);
1805 uret[0]->lastcall = extract_long(cret, 7);
1806 uret[0]->USuserpurge = extract_int(cret, 8);
1813 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1818 if (!cret) return -2;
1819 if (!uret) return -2;
1821 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1822 if (!aaa) return -1;
1824 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1825 uret->fullname, uret->password, uret->flags,
1826 uret->timescalled, uret->posted, uret->axlevel,
1827 uret->usernum, uret->lastcall, uret->USuserpurge);
1828 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1835 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1836 /* caller must free the struct ExpirePolicy */
1837 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1838 struct ExpirePolicy **policy, char *cret)
1840 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1844 if (!cret) return -2;
1845 if (!policy) return -2;
1846 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1847 if (!*policy) return -1;
1848 if (which < 0 || which > 3) return -2;
1850 sprintf(cmd, "GPEX %s", proto[which]);
1851 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1852 if (ret / 100 == 2) {
1853 policy[0]->expire_mode = extract_int(cret, 0);
1854 policy[0]->expire_value = extract_int(cret, 1);
1861 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1862 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1863 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1864 struct ExpirePolicy *policy, char *cret)
1867 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1869 if (!cret) return -2;
1870 if (which < 0 || which > 3) return -2;
1871 if (!policy) return -2;
1872 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1873 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1875 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1876 policy->expire_mode, policy->expire_value);
1877 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1882 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1886 if (!cret) return -2;
1887 if (!listing) return -2;
1888 if (*listing) return -2;
1890 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1891 listing, &bytes, cret);
1896 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1898 if (!cret) return -2;
1899 if (!listing) return -2;
1901 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1907 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1908 char **listing, char *cret)
1914 if (!cret) return -2;
1915 if (!mimetype) return -2;
1916 if (!listing) return -2;
1917 if (*listing) return -2;
1919 aaa = malloc(strlen(mimetype) + 13);
1920 if (!aaa) return -1;
1921 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1922 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1923 listing, &bytes, cret);
1930 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1931 const char *listing, char *cret)
1936 if (!cret) return -2;
1937 if (!mimetype) return -2;
1938 if (!listing) return -2;
1940 aaa = malloc(strlen(mimetype) + 13);
1941 if (!aaa) return -1;
1942 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1943 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1951 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1955 if (!cret) return -2;
1956 if (!listing) return -2;
1957 if (*listing) return -2;
1959 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1960 listing, &bytes, cret);
1965 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1967 if (!cret) return -2;
1968 if (!listing) return -2;
1970 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1976 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1980 if (!cret) return -2;
1981 if (session < 0) return -2;
1983 sprintf(aaa, "REQT %d", session);
1984 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1989 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1993 if (!cret) return -2;
1994 if (msgnum < 0) return -2;
1996 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1997 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2002 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
2011 /* New SSL object */
2012 temp_ssl = SSL_new(ssl_ctx);
2014 error_printf("SSL_new failed: %s\n",
2015 ERR_reason_error_string(ERR_get_error()));
2018 /* Pointless flag waving */
2019 #if SSLEAY_VERSION_NUMBER >= 0x0922
2020 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
2023 if (!access(EGD_POOL, F_OK))
2026 if (!RAND_status()) {
2027 error_printf("PRNG not properly seeded\n");
2031 /* Associate network connection with SSL object */
2032 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
2033 error_printf("SSL_set_fd failed: %s\n",
2034 ERR_reason_error_string(ERR_get_error()));
2038 if (status_hook != NULL)
2039 status_hook("Requesting encryption...\r");
2041 /* Ready to start SSL/TLS */
2043 CtdlIPC_putline(ipc, "STLS");
2044 CtdlIPC_getline(ipc, buf);
2045 if (buf[0] != '2') {
2046 error_printf("Server can't start TLS: %s\n", buf);
2050 r = CtdlIPCGenericCommand(ipc,
2051 "STLS", NULL, 0, NULL, NULL, cret);
2053 error_printf("Server can't start TLS: %s\n", buf);
2058 /* Do SSL/TLS handshake */
2059 if ((a = SSL_connect(temp_ssl)) < 1) {
2060 error_printf("SSL_connect failed: %s\n",
2061 ERR_reason_error_string(ERR_get_error()));
2065 ipc->ssl = temp_ssl;
2067 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2071 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2072 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2073 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2074 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2080 #endif /* HAVE_OPENSSL */
2085 static void endtls(SSL *ssl)
2096 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2101 if (!address) return -2;
2102 if (!cret) return -2;
2104 aaa = (char *)malloc(strlen(address) + 6);
2105 if (!aaa) return -1;
2107 sprintf(aaa, "QDIR %s", address);
2108 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2115 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2119 if (!cret) return -2;
2120 sprintf(aaa, "IPGM %d", secret);
2121 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2126 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2130 if (!cret) return -2;
2131 if (!mret) return -2;
2132 if (*mret) return -2;
2134 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2155 /* ************************************************************************** */
2156 /* Stuff below this line is not for public consumption */
2157 /* ************************************************************************** */
2160 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2162 if (ipc->network_status_cb) ipc->network_status_cb(1);
2163 #ifdef THREADED_CLIENT
2164 pthread_mutex_lock(&(ipc->mutex));
2169 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2171 #ifdef THREADED_CLIENT
2172 pthread_mutex_unlock(&(ipc->mutex));
2174 if (ipc->network_status_cb) ipc->network_status_cb(0);
2178 /* Read a listing from the server up to 000. Append to dest if it exists */
2179 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2188 length = strlen(ret);
2193 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2194 linelength = strlen(aaa);
2195 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2197 strcpy(&ret[length], aaa);
2198 length += linelength;
2199 strcpy(&ret[length++], "\n");
2207 /* Send a listing to the server; generate the ending 000. */
2208 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2212 text = (char *)malloc(strlen(listing) + 6);
2214 strcpy(text, listing);
2215 while (text[strlen(text) - 1] == '\n')
2216 text[strlen(text) - 1] = '\0';
2217 strcat(text, "\n000");
2218 CtdlIPC_putline(ipc, text);
2222 /* Malloc failed but we are committed to send */
2223 /* This may result in extra blanks at the bottom */
2224 CtdlIPC_putline(ipc, text);
2225 CtdlIPC_putline(ipc, "000");
2231 /* Partial read of file from server */
2232 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2234 register size_t len = 0;
2238 if (!cret) return 0;
2239 if (bytes < 1) return 0;
2242 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2243 CtdlIPC_putline(ipc, aaa);
2244 CtdlIPC_getline(ipc, aaa);
2246 strcpy(cret, &aaa[4]);
2248 len = extract_long(&aaa[4], 0);
2249 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2251 /* I know what I'm doing */
2252 serv_read(ipc, ((char *)(*buf) + offset), len);
2254 /* We have to read regardless */
2255 serv_read(ipc, aaa, len);
2259 CtdlIPC_unlock(ipc);
2265 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2269 if (!cret) return -2;
2270 if (!ipc->downloading) return -2;
2272 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2274 ipc->downloading = 0;
2280 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2284 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2285 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2292 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2293 void (*progress_gauge_callback)
2294 (CtdlIPC*, unsigned long, unsigned long),
2297 register size_t len;
2299 if (!cret) return -1;
2300 if (!buf) return -1;
2301 if (*buf) return -1;
2302 if (!ipc->downloading) return -1;
2305 if (progress_gauge_callback)
2306 progress_gauge_callback(ipc, len, bytes);
2307 while (len < bytes) {
2308 register size_t block;
2310 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2316 if (progress_gauge_callback)
2317 progress_gauge_callback(ipc, len, bytes);
2322 /* READ - pipelined */
2323 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2325 void (*progress_gauge_callback)
2326 (CtdlIPC*, unsigned long, unsigned long),
2329 register size_t len;
2330 register int calls; /* How many calls in the pipeline */
2331 register int i; /* iterator */
2334 if (!cret) return -1;
2335 if (!buf) return -1;
2336 if (*buf) return -1;
2337 if (!ipc->downloading) return -1;
2339 *buf = (void *)realloc(*buf, bytes - resume);
2340 if (!*buf) return -1;
2344 if (progress_gauge_callback)
2345 progress_gauge_callback(ipc, len, bytes);
2347 /* How many calls will be in the pipeline? */
2348 calls = (bytes - resume) / 4096;
2349 if ((bytes - resume) % 4096) calls++;
2351 /* Send all requests at once */
2352 for (i = 0; i < calls; i++) {
2353 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2354 CtdlIPC_putline(ipc, aaa);
2357 /* Receive all responses at once */
2358 for (i = 0; i < calls; i++) {
2359 CtdlIPC_getline(ipc, aaa);
2361 strcpy(cret, &aaa[4]);
2363 len = extract_long(&aaa[4], 0);
2364 /* I know what I'm doing */
2365 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2367 if (progress_gauge_callback)
2368 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2370 CtdlIPC_unlock(ipc);
2376 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2381 if (!cret) return -1;
2382 if (!ipc->uploading) return -1;
2384 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2385 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2392 int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
2393 void (*progress_gauge_callback)
2394 (CtdlIPC*, unsigned long, unsigned long),
2397 register int ret = -1;
2398 register size_t offset = 0;
2402 FILE *fd = uploadFP;
2405 if (!cret) return -1;
2407 fseek(fd, 0L, SEEK_END);
2411 if (progress_gauge_callback)
2412 progress_gauge_callback(ipc, 0, bytes);
2414 while (offset < bytes) {
2415 register size_t to_write;
2417 /* Read some data in */
2418 to_write = fread(buf, 1, 4096, fd);
2420 if (feof(fd) || ferror(fd)) break;
2422 sprintf(aaa, "WRIT %d", (int)to_write);
2423 CtdlIPC_putline(ipc, aaa);
2424 CtdlIPC_getline(ipc, aaa);
2425 strcpy(cret, &aaa[4]);
2427 if (aaa[0] == '7') {
2428 to_write = extract_long(&aaa[4], 0);
2430 serv_write(ipc, buf, to_write);
2432 if (progress_gauge_callback)
2433 progress_gauge_callback(ipc, offset, bytes);
2434 /* Detect short reads and back up if needed */
2435 /* offset will never be negative anyway */
2436 fseek(fd, (signed)offset, SEEK_SET);
2441 if (progress_gauge_callback)
2442 progress_gauge_callback(ipc, 1, 1);
2445 return (!ferr ? ret : -2);
2450 * Generic command method. This method should handle any server command
2451 * except for CHAT. It takes the following arguments:
2453 * ipc The server to speak with
2454 * command Preformatted command to send to server
2455 * to_send A text or binary file to send to server
2456 * (only sent if server requests it)
2457 * bytes_to_send The number of bytes in to_send (required if
2458 * sending binary, optional if sending listing)
2459 * to_receive Pointer to a NULL pointer, if the server
2460 * sends text or binary we will allocate memory
2461 * for the file and stuff it here
2462 * bytes_to_receive If a file is received, we will store its
2464 * proto_response The protocol response. Caller must provide
2465 * this buffer and ensure that it is at least
2466 * 128 bytes in length.
2468 * This function returns a number equal to the protocol response number,
2469 * -1 if an internal error occurred, -2 if caller provided bad values,
2470 * or 0 - the protocol response number if bad values were found during
2471 * the protocol exchange.
2472 * It stores the protocol response string (minus the number) in
2473 * protocol_response as described above. Some commands send additional
2474 * data in this string.
2476 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2477 const char *command, const char *to_send,
2478 size_t bytes_to_send, char **to_receive,
2479 size_t *bytes_to_receive, char *proto_response)
2485 if (!command) return -2;
2486 if (!proto_response) return -2;
2489 if (ipc->ssl) watch_ssl = 1;
2493 CtdlIPC_putline(ipc, command);
2495 CtdlIPC_getline(ipc, proto_response);
2496 if (proto_response[3] == '*')
2498 ret = atoi(proto_response);
2499 strcpy(proto_response, &proto_response[4]);
2500 switch (ret / 100) {
2501 default: /* Unknown, punt */
2503 case 3: /* MORE_DATA */
2505 /* Don't need to do anything */
2507 case 1: /* LISTING_FOLLOWS */
2508 if (to_receive && !*to_receive && bytes_to_receive) {
2509 *to_receive = CtdlIPCReadListing(ipc, NULL);
2510 } else { /* Drain */
2511 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2515 case 4: /* SEND_LISTING */
2517 CtdlIPCSendListing(ipc, to_send);
2519 /* No listing given, fake it */
2520 CtdlIPC_putline(ipc, "000");
2524 case 6: /* BINARY_FOLLOWS */
2525 if (to_receive && !*to_receive && bytes_to_receive) {
2527 extract_long(proto_response, 0);
2528 *to_receive = (char *)
2529 malloc((size_t)*bytes_to_receive);
2533 serv_read(ipc, *to_receive,
2540 drain = extract_long(proto_response, 0);
2541 while (drain > SIZ) {
2542 serv_read(ipc, buf, SIZ);
2545 serv_read(ipc, buf, drain);
2549 case 7: /* SEND_BINARY */
2550 if (to_send && bytes_to_send) {
2551 serv_write(ipc, to_send, bytes_to_send);
2552 } else if (bytes_to_send) {
2553 /* Fake it, send nulls */
2556 fake = bytes_to_send;
2557 memset(buf, '\0', SIZ);
2558 while (fake > SIZ) {
2559 serv_write(ipc, buf, SIZ);
2562 serv_write(ipc, buf, fake);
2564 } /* else who knows? DANGER WILL ROBINSON */
2566 case 8: /* START_CHAT_MODE */
2567 if (!strncasecmp(command, "CHAT", 4)) {
2568 /* Don't call chatmode with generic! */
2569 CtdlIPC_putline(ipc, "/quit");
2572 /* In this mode we send then receive listing */
2574 CtdlIPCSendListing(ipc, to_send);
2576 /* No listing given, fake it */
2577 CtdlIPC_putline(ipc, "000");
2580 if (to_receive && !*to_receive
2581 && bytes_to_receive) {
2582 *to_receive = CtdlIPCReadListing(ipc, NULL);
2583 } else { /* Drain */
2584 while (CtdlIPC_getline(ipc, buf),
2585 strcmp(buf, "000")) ;
2590 case 9: /* ASYNC_MSG */
2591 /* CtdlIPCDoAsync(ret, proto_response); */
2592 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2598 CtdlIPC_unlock(ipc);
2603 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2605 struct hostent *phe;
2606 struct servent *pse;
2607 struct protoent *ppe;
2608 struct sockaddr_in sin;
2611 memset(&sin, 0, sizeof(sin));
2612 sin.sin_family = AF_INET;
2614 pse = getservbyname(service, protocol);
2616 sin.sin_port = pse->s_port;
2618 else if (atoi(service) > 0) {
2619 sin.sin_port = htons(atoi(service));
2622 sin.sin_port = htons(defaultPort);
2624 phe = gethostbyname(host);
2626 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2627 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2630 if ((ppe = getprotobyname(protocol)) == 0) {
2633 if (!strcmp(protocol, "udp")) {
2639 s = socket(PF_INET, type, ppe->p_proto);
2644 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2652 static int uds_connectsock(int *isLocal, char *sockpath)
2654 struct sockaddr_un addr;
2657 memset(&addr, 0, sizeof(addr));
2658 addr.sun_family = AF_UNIX;
2659 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2661 s = socket(AF_UNIX, SOCK_STREAM, 0);
2666 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2677 * input binary data from socket
2679 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2681 unsigned int len, rlen;
2683 #if defined(HAVE_OPENSSL)
2685 serv_read_ssl(ipc, buf, bytes);
2690 while (len < bytes) {
2691 rlen = read(ipc->sock, &buf[len], bytes - len);
2693 connection_died(ipc, 0);
2702 * send binary to server
2704 void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2706 unsigned int bytes_written = 0;
2709 #if defined(HAVE_OPENSSL)
2711 serv_write_ssl(ipc, buf, nbytes);
2715 while (bytes_written < nbytes) {
2716 retval = write(ipc->sock, &buf[bytes_written],
2717 nbytes - bytes_written);
2719 connection_died(ipc, 0);
2722 bytes_written += retval;
2729 * input binary data from encrypted connection
2731 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2737 while (len < bytes) {
2738 if (SSL_want_read(ipc->ssl)) {
2739 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2740 error_printf("SSL_write in serv_read:\n");
2741 ERR_print_errors_fp(stderr);
2744 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2748 errval = SSL_get_error(ipc->ssl, rlen);
2749 if (errval == SSL_ERROR_WANT_READ ||
2750 errval == SSL_ERROR_WANT_WRITE) {
2755 Not sure why we'd want to handle these error codes any differently,
2756 but this definitely isn't the way to handle them. Someone must have
2757 naively assumed that we could fall back to unencrypted communications,
2758 but all it does is just recursively blow the stack.
2759 if (errval == SSL_ERROR_ZERO_RETURN ||
2760 errval == SSL_ERROR_SSL) {
2761 serv_read(ipc, &buf[len], bytes - len);
2765 error_printf("SSL_read in serv_read: %s\n",
2766 ERR_reason_error_string(ERR_peek_error()));
2767 connection_died(ipc, 1);
2776 * send binary to server encrypted
2778 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2780 unsigned int bytes_written = 0;
2784 while (bytes_written < nbytes) {
2785 if (SSL_want_write(ipc->ssl)) {
2786 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2787 error_printf("SSL_read in serv_write:\n");
2788 ERR_print_errors_fp(stderr);
2791 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2792 nbytes - bytes_written);
2796 errval = SSL_get_error(ipc->ssl, retval);
2797 if (errval == SSL_ERROR_WANT_READ ||
2798 errval == SSL_ERROR_WANT_WRITE) {
2802 if (errval == SSL_ERROR_ZERO_RETURN ||
2803 errval == SSL_ERROR_SSL) {
2804 serv_write(ipc, &buf[bytes_written],
2805 nbytes - bytes_written);
2808 error_printf("SSL_write in serv_write: %s\n",
2809 ERR_reason_error_string(ERR_peek_error()));
2810 connection_died(ipc, 1);
2813 bytes_written += retval;
2818 #ifdef THREADED_CLIENT
2819 static void ssl_lock(int mode, int n, const char *file, int line)
2821 if (mode & CRYPTO_LOCK)
2822 pthread_mutex_lock(Critters[n]);
2824 pthread_mutex_unlock(Critters[n]);
2826 #endif /* THREADED_CLIENT */
2829 static void CtdlIPC_init_OpenSSL(void)
2832 SSL_METHOD *ssl_method;
2835 /* already done init */
2844 SSL_load_error_strings();
2845 SSLeay_add_ssl_algorithms();
2847 /* Set up the SSL context in which we will oeprate */
2848 ssl_method = SSLv23_client_method();
2849 ssl_ctx = SSL_CTX_new(ssl_method);
2851 error_printf("SSL_CTX_new failed: %s\n",
2852 ERR_reason_error_string(ERR_get_error()));
2855 /* Any reasonable cipher we can get */
2856 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2857 error_printf("No ciphers available for encryption\n");
2860 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2862 /* Load DH parameters into the context */
2865 error_printf("Can't allocate a DH object: %s\n",
2866 ERR_reason_error_string(ERR_get_error()));
2869 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2870 error_printf("Can't assign DH_P: %s\n",
2871 ERR_reason_error_string(ERR_get_error()));
2875 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2876 error_printf("Can't assign DH_G: %s\n",
2877 ERR_reason_error_string(ERR_get_error()));
2882 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2885 #ifdef THREADED_CLIENT
2886 /* OpenSSL requires callbacks for threaded clients */
2887 CRYPTO_set_locking_callback(ssl_lock);
2888 CRYPTO_set_id_callback(id_callback);
2890 /* OpenSSL requires us to do semaphores for threaded clients */
2891 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2893 perror("malloc failed");
2896 for (a = 0; a < CRYPTO_num_locks(); a++) {
2897 Critters[a] = malloc(sizeof (pthread_mutex_t));
2899 perror("malloc failed");
2902 pthread_mutex_init(Critters[a], NULL);
2905 #endif /* THREADED_CLIENT */
2910 #ifdef THREADED_CLIENT
2911 static unsigned long id_callback(void) {
2912 return (unsigned long)pthread_self();
2914 #endif /* THREADED_CLIENT */
2915 #endif /* HAVE_OPENSSL */
2919 ReadNetworkChunk(CtdlIPC* ipc)
2936 FD_SET(ipc->sock, &read_fd);
2937 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2939 // fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
2943 *(ipc->BufPtr) = '\0';
2944 // n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2945 n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
2947 ipc->BufPtr[n]='\0';
2955 if (!(errno == EINTR || errno == EAGAIN))
2956 error_printf( "\nselect failed: %d %s\n", err, strerror(err));
2962 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2964 ipc->BufPtr[n]='\0';
2969 connection_died(ipc, 0);
2977 * input string from socket - implemented in terms of serv_read()
2981 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2984 char *aptr, *bptr, *aeptr, *beptr;
2986 // error_printf("---\n");
2989 #if defined(HAVE_OPENSSL)
2992 /* Read one character at a time. */
2994 serv_read(ipc, &buf[i], 1);
2995 if (buf[i] == '\n' || i == (SIZ-1))
2999 /* If we got a long line, discard characters until the newline. */
3001 while (buf[i] != '\n')
3002 serv_read(ipc, &buf[i], 1);
3004 /* Strip the trailing newline (and carriage return, if present) */
3005 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3006 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3011 if (ipc->Buf == NULL)
3014 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
3016 ipc->BufPtr = ipc->Buf;
3020 // while ((ipc->BufUsed == 0)||(ntries++ > 10))
3021 if (ipc->BufUsed == 0)
3022 ReadNetworkChunk(ipc);
3024 //// if (ipc->BufUsed != 0) while (1)
3030 aeptr = ipc->Buf + ipc->BufSize;
3031 while ((aptr < aeptr) &&
3035 *(bptr++) = *(aptr++);
3036 if ((*aptr == '\n') && (aptr < aeptr))
3038 /* Terminate it right, remove the line breaks */
3039 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
3041 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
3044 // 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);
3045 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
3048 /* is there more in the buffer we need to read later? */
3049 if (ipc->Buf + ipc->BufUsed > aptr)
3056 ipc->BufPtr = ipc->Buf;
3058 // error_printf("----bla6\n");
3061 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
3062 else if ((ipc->BufPtr != ipc->Buf) &&
3063 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
3065 size_t NewBufSize = ipc->BufSize * 2;
3066 int delta = (ipc->BufPtr - ipc->Buf);
3069 /* if the line would end after our buffer, we should use a bigger buffer. */
3070 NewBuf = (char *)malloc (NewBufSize + 10);
3071 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
3073 ipc->Buf = ipc->BufPtr = NewBuf;
3074 ipc->BufUsed -= delta;
3075 ipc->BufSize = NewBufSize;
3077 if (ReadNetworkChunk(ipc) <0)
3079 // error_printf("----bla\n");
3083 /// error_printf("----bl45761%s\nipc->BufUsed");
3085 // error_printf("----bla1\n");
3088 #else /* CHUNKED_READ */
3090 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
3094 /* Read one character at a time. */
3096 serv_read(ipc, &buf[i], 1);
3097 if (buf[i] == '\n' || i == (SIZ-1))
3101 /* If we got a long line, discard characters until the newline. */
3103 while (buf[i] != '\n')
3104 serv_read(ipc, &buf[i], 1);
3106 /* Strip the trailing newline (and carriage return, if present) */
3107 if (i>=0 && buf[i] == 10) buf[i--] = 0;
3108 if (i>=0 && buf[i] == 13) buf[i--] = 0;
3112 #endif /* CHUNKED_READ */
3115 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
3117 CtdlIPC_getline(ipc, buf);
3121 * send line to server - implemented in terms of serv_write()
3123 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3129 cmd = malloc(len + 2);
3131 /* This requires no extra memory */
3132 serv_write(ipc, buf, len);
3133 serv_write(ipc, "\n", 1);
3135 /* This is network-optimized */
3136 strncpy(cmd, buf, len);
3137 strcpy(cmd + len, "\n");
3138 serv_write(ipc, cmd, len + 1);
3142 ipc->last_command_sent = time(NULL);
3145 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3147 CtdlIPC_putline(ipc, buf);
3154 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3162 ipc = ialloc(CtdlIPC);
3166 #if defined(HAVE_OPENSSL)
3168 CtdlIPC_init_OpenSSL();
3170 #if defined(HAVE_PTHREAD_H)
3171 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3173 ipc->sock = -1; /* Not connected */
3174 ipc->isLocal = 0; /* Not local, of course! */
3175 ipc->downloading = 0;
3177 ipc->last_command_sent = 0L;
3178 ipc->network_status_cb = NULL;
3183 strcpy(cithost, DEFAULT_HOST); /* default host */
3184 strcpy(citport, DEFAULT_PORT); /* default port */
3186 /* Allow caller to supply our values (Windows) */
3187 if (hostbuf && strlen(hostbuf) > 0)
3188 strcpy(cithost, hostbuf);
3189 if (portbuf && strlen(portbuf) > 0)
3190 strcpy(citport, portbuf);
3192 /* Read host/port from command line if present */
3193 for (a = 0; a < argc; ++a) {
3196 } else if (a == 1) {
3197 strcpy(cithost, argv[a]);
3198 } else if (a == 2) {
3199 strcpy(citport, argv[a]);
3201 error_printf("%s: usage: ",argv[0]);
3202 error_printf("%s [host] [port] ",argv[0]);
3209 if ((!strcmp(cithost, "localhost"))
3210 || (!strcmp(cithost, "127.0.0.1"))) {
3214 /* If we're using a unix domain socket we can do a bunch of stuff */
3215 if (!strcmp(cithost, UDS)) {
3216 if (!strcasecmp(citport, DEFAULT_PORT)) {
3217 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3220 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3222 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3223 if (ipc->sock == -1) {
3227 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3228 if (portbuf != NULL) strcpy(portbuf, sockpath);
3232 ipc->sock = connectsock(cithost, citport, "tcp", 504);
3233 if (ipc->sock == -1) {
3237 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3238 if (portbuf != NULL) strcpy(portbuf, citport);
3244 * Disconnect and delete the IPC class (destructor)
3246 void CtdlIPC_delete(CtdlIPC* ipc)
3250 SSL_shutdown(ipc->ssl);
3255 if (ipc->sock > -1) {
3256 shutdown(ipc->sock, 2); /* Close it up */
3259 if (ipc->Buf != NULL)
3268 * Disconnect and delete the IPC class (destructor)
3269 * Also NULLs out the pointer
3271 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3273 CtdlIPC_delete(*pipc);
3279 * return the file descriptor of the server socket so we can select() on it.
3281 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3284 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3291 * return one character
3293 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3296 char CtdlIPC_get(CtdlIPC* ipc)
3301 serv_read(ipc, buf, 1);