16 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25 #include <netinet/in.h>
29 #ifdef THREADED_CLIENT
33 #include "citadel_ipc.h"
34 #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 CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
372 struct ctdlipcroom **rret, char *cret)
377 if (!cret) return -2;
378 if (!rret) return -2;
379 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
380 if (!*rret) return -1;
383 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
388 sprintf(aaa, "GOTO %s|%s", room, passwd);
390 aaa = (char *)malloc(strlen(room) + 6);
395 sprintf(aaa, "GOTO %s", room);
397 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
398 if (ret / 100 == 2) {
399 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
400 rret[0]->RRunread = extract_long(cret, 1);
401 rret[0]->RRtotal = extract_long(cret, 2);
402 rret[0]->RRinfoupdated = extract_int(cret, 3);
403 rret[0]->RRflags = extract_int(cret, 4);
404 rret[0]->RRhighest = extract_long(cret, 5);
405 rret[0]->RRlastread = extract_long(cret, 6);
406 rret[0]->RRismailbox = extract_int(cret, 7);
407 rret[0]->RRaide = extract_int(cret, 8);
408 rret[0]->RRnewmail = extract_long(cret, 9);
409 rret[0]->RRfloor = extract_int(cret, 10);
410 rret[0]->RRflags2 = extract_int(cret, 14);
421 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
422 /* whicharg is number of messages, applies to last, first, gt, lt */
423 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
424 const char *mtemplate, unsigned long **mret, char *cret)
427 register unsigned long count = 0;
428 static char *proto[] =
429 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
434 if (!cret) return -2;
435 if (!mret) return -2;
436 if (*mret) return -2;
437 if (which < 0 || which > 6) return -2;
440 sprintf(aaa, "MSGS %s||%d", proto[which],
441 (mtemplate) ? 1 : 0);
443 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
444 (mtemplate) ? 1 : 0);
445 if (mtemplate) count = strlen(mtemplate);
446 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
450 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
453 while (bbb && strlen(bbb)) {
454 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
455 remove_token(bbb, 0, '\n');
456 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
457 sizeof (unsigned long)));
459 (*mret)[count++] = atol(aaa);
471 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
472 struct ctdlipcmessage **mret, char *cret)
478 int multipart_hunting = 0;
479 char multipart_prefix[128];
481 if (!cret) return -1;
482 if (!mret) return -1;
483 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
484 if (!*mret) return -1;
485 if (!msgnum) return -1;
487 strcpy(mret[0]->content_type, "");
488 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
489 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
490 if (ret / 100 == 1) {
492 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
493 while (strlen(bbb) > 4 && bbb[4] == '=') {
494 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
495 remove_token(bbb, 0, '\n');
497 if (!strncasecmp(aaa, "nhdr=yes", 8))
499 else if (!strncasecmp(aaa, "from=", 5))
500 safestrncpy(mret[0]->author, &aaa[5], SIZ);
501 else if (!strncasecmp(aaa, "type=", 5))
502 mret[0]->type = atoi(&aaa[5]);
503 else if (!strncasecmp(aaa, "msgn=", 5))
504 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
505 else if (!strncasecmp(aaa, "subj=", 5))
506 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
507 else if (!strncasecmp(aaa, "rfca=", 5))
508 safestrncpy(mret[0]->email, &aaa[5], SIZ);
509 else if (!strncasecmp(aaa, "hnod=", 5))
510 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
511 else if (!strncasecmp(aaa, "room=", 5))
512 safestrncpy(mret[0]->room, &aaa[5], SIZ);
513 else if (!strncasecmp(aaa, "node=", 5))
514 safestrncpy(mret[0]->node, &aaa[5], SIZ);
515 else if (!strncasecmp(aaa, "rcpt=", 5))
516 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
517 else if (!strncasecmp(aaa, "time=", 5))
518 mret[0]->time = atol(&aaa[5]);
520 /* Multipart/alternative prefix & suffix strings help
521 * us to determine which part we want to download.
523 else if (!strncasecmp(aaa, "pref=", 5)) {
524 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
525 if (!strcasecmp(multipart_prefix,
526 "multipart/alternative")) {
530 else if (!strncasecmp(aaa, "suff=", 5)) {
531 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
532 if (!strcasecmp(multipart_prefix,
533 "multipart/alternative")) {
538 else if (!strncasecmp(aaa, "part=", 5)) {
539 struct parts *ptr, *chain;
541 ptr = (struct parts *)calloc(1, sizeof (struct parts));
544 /* Fill the buffers for the caller */
545 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
546 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
547 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
548 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
549 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
550 ptr->length = extract_long(&aaa[5], 5);
551 if (!mret[0]->attachments)
552 mret[0]->attachments = ptr;
554 chain = mret[0]->attachments;
560 /* Now handle multipart/alternative */
561 if (multipart_hunting > 0) {
562 if ( (!strcasecmp(ptr->mimetype,
564 || (!strcasecmp(ptr->mimetype,
566 strcpy(mret[0]->mime_chosen,
574 /* Eliminate "text\n" */
575 remove_token(bbb, 0, '\n');
577 /* If doing a MIME thing, pull out the extra headers */
580 if (!strncasecmp(bbb, "Content-type:", 13)) {
581 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
582 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
583 striplt(mret[0]->content_type);
585 /* strip out ";charset=" portion. FIXME do something with
586 * the charset (like... convert it) instead of just throwing
589 if (strstr(mret[0]->content_type, ";") != NULL) {
590 strcpy(strstr(mret[0]->content_type, ";"), "");
594 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
595 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
596 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
597 striplt(mret[0]->mime_chosen);
599 remove_token(bbb, 0, '\n');
600 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
601 remove_token(bbb, 0, '\n');
607 /* FIXME: Strip trailing whitespace */
608 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
610 bbb = (char *)realloc(bbb, 1);
620 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
625 if (!cret) return -2;
626 if (!listing) return -2;
627 if (*listing) return -2;
629 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
635 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
639 char *listing = NULL;
642 if (!cret) return -2;
644 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
645 if (ret / 100 == 1) {
648 while (*listing && strlen(listing)) {
649 extract_token(buf, listing, 0, '\n', sizeof buf);
650 remove_token(listing, 0, '\n');
652 case 0: ipc->ServInfo.pid = atoi(buf);
654 case 1: strcpy(ipc->ServInfo.nodename,buf);
656 case 2: strcpy(ipc->ServInfo.humannode,buf);
658 case 3: strcpy(ipc->ServInfo.fqdn,buf);
660 case 4: strcpy(ipc->ServInfo.software,buf);
662 case 5: ipc->ServInfo.rev_level = atoi(buf);
664 case 6: strcpy(ipc->ServInfo.site_location,buf);
666 case 7: strcpy(ipc->ServInfo.sysadm,buf);
668 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
670 case 10: ipc->ServInfo.ok_floors = atoi(buf);
672 case 11: ipc->ServInfo.paging_level = atoi(buf);
674 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
676 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
678 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
684 if (listing) free(listing);
690 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
695 if (!cret) return -2;
696 if (!listing) return -2;
697 if (*listing) return -2;
699 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
705 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
707 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
712 if (!cret) return -2;
715 sprintf(aaa, "SLRP %ld", msgnum);
718 sprintf(aaa, "SLRP HIGHEST");
720 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
726 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
731 if (!cret) return -2;
732 if (!username) return -2;
734 aaa = (char *)malloc(strlen(username) + 6);
737 sprintf(aaa, "INVT %s", username);
738 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
745 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
750 if (!cret) return -1;
751 if (!username) return -1;
753 aaa = (char *)malloc(strlen(username) + 6);
755 sprintf(aaa, "KICK %s", username);
756 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
763 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
767 if (!cret) return -2;
768 if (!qret) return -2;
769 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
770 if (!*qret) return -1;
772 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
773 if (ret / 100 == 2) {
774 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
775 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
776 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
777 qret[0]->QRflags = extract_int(cret, 3);
778 qret[0]->QRfloor = extract_int(cret, 4);
779 qret[0]->QRorder = extract_int(cret, 5);
780 qret[0]->QRdefaultview = extract_int(cret, 6);
781 qret[0]->QRflags2 = extract_int(cret, 7);
788 /* set forget to kick all users out of room */
789 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
794 if (!cret) return -2;
795 if (!qret) return -2;
797 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
798 strlen(qret->QRdirname) + 64);
801 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
802 qret->QRname, qret->QRpasswd, qret->QRdirname,
803 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
804 qret->QRdefaultview, qret->QRflags2);
805 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
812 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
814 if (!cret) return -1;
816 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
821 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
826 if (!cret) return -2;
827 if (!username) return -2;
829 aaa = (char *)malloc(strlen(username) + 6);
832 sprintf(aaa, "SETA %s", username);
833 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
840 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, const struct ctdlipcmessage *mr, char *cret)
845 if (!cret) return -2;
848 snprintf(cmd, sizeof cmd,
849 "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
850 mr->anonymous, mr->type, mr->subject, mr->author);
851 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
853 if ((flag == 0) && (subject_required != NULL)) {
854 /* Is the server strongly recommending that the user enter a message subject? */
855 if ((cret[3] != '\0') && (cret[4] != '\0')) {
856 *subject_required = extract_int(&cret[4], 1);
866 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
870 if (!cret) return -2;
871 if (!iret) return -2;
872 if (*iret) return -2;
874 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
879 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
883 if (!cret) return -2;
884 if (!msgnum) return -2;
886 sprintf(aaa, "DELE %ld", msgnum);
887 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
892 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
897 if (!cret) return -2;
898 if (!destroom) return -2;
899 if (!msgnum) return -2;
901 aaa = (char *)malloc(strlen(destroom) + 28);
904 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
905 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
912 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
916 if (!cret) return -2;
918 sprintf(aaa, "KILL %d", for_real);
919 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
924 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
925 const char *password, int floor, char *cret)
930 if (!cret) return -2;
931 if (!roomname) return -2;
934 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
936 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
939 aaa = (char *)malloc(strlen(roomname) + 40);
941 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
944 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
951 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
953 if (!cret) return -2;
955 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
960 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
966 if (!cret) return -2;
967 if (!mret) return -2;
968 if (*mret) return -2;
969 if (!message) return -2;
971 aaa = (char *)malloc(strlen(message) + 6);
974 sprintf(aaa, "MESG %s", message);
975 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
982 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
984 if (!cret) return -2;
986 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
991 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
997 if (!cret) return -2;
998 if (!rret) return -2;
999 if (*rret) return -2;
1002 aaa = (char *)malloc(strlen(username) + 6);
1004 aaa = (char *)malloc(12);
1005 if (!aaa) return -1;
1008 sprintf(aaa, "GREG %s", username);
1010 sprintf(aaa, "GREG _SELF_");
1011 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1018 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1023 if (!cret) return -2;
1024 if (!username) return -2;
1025 if (axlevel < 0 || axlevel > 7) return -2;
1027 aaa = (char *)malloc(strlen(username) + 17);
1028 if (!aaa) return -1;
1030 sprintf(aaa, "VALI %s|%d", username, axlevel);
1031 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1038 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1042 if (!cret) return -1;
1043 if (!info) return -1;
1045 sprintf(aaa, "EINF %d", for_real);
1046 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1051 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1057 if (!cret) return -1;
1058 if (!listing) return -1;
1059 if (*listing) return -1;
1060 if (!searchstring) return -1;
1062 cmd = malloc(strlen(searchstring) + 10);
1063 sprintf(cmd, "LIST %s", searchstring);
1065 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1072 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1074 if (!cret) return -1;
1075 if (!info) return -1;
1077 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1083 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1087 if (!cret) return -1;
1088 if (!chek) return -1;
1090 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1091 if (ret / 100 == 2) {
1092 chek->newmail = extract_long(cret, 0);
1093 chek->needregis = extract_int(cret, 1);
1094 chek->needvalid = extract_int(cret, 2);
1101 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1106 if (!cret) return -2;
1107 if (!filename) return -2;
1109 aaa = (char *)malloc(strlen(filename) + 6);
1110 if (!aaa) return -1;
1112 sprintf(aaa, "DELF %s", filename);
1113 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1120 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1125 if (!cret) return -2;
1126 if (!filename) return -2;
1127 if (!destroom) return -2;
1129 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1130 if (!aaa) return -1;
1132 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1133 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1140 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1145 if (!cret) return -2;
1146 if (!filename) return -2;
1147 if (!destnode) return -2;
1149 aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1150 if (!aaa) return -1;
1152 sprintf(aaa, "NETF %s|%s", filename, destnode);
1153 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1160 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1165 if (!cret) return -1;
1166 if (!listing) return -1;
1167 if (*listing) return -1;
1169 *stamp = CtdlIPCServerTime(ipc, cret);
1171 *stamp = time(NULL);
1172 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1178 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1180 void (*progress_gauge_callback)
1181 (CtdlIPC*, unsigned long, unsigned long),
1190 if (!cret) return -2;
1191 if (!filename) return -2;
1192 if (!buf) return -2;
1193 if (*buf) return -2;
1194 if (ipc->downloading) return -2;
1196 aaa = (char *)malloc(strlen(filename) + 6);
1197 if (!aaa) return -1;
1199 sprintf(aaa, "OPEN %s", filename);
1200 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1202 if (ret / 100 == 2) {
1203 ipc->downloading = 1;
1204 bytes = extract_long(cret, 0);
1205 last_mod = extract_int(cret, 1);
1206 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1208 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1209 progress_gauge_callback, cret);
1211 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1212 progress_gauge_callback, cret);
1215 ret = CtdlIPCEndDownload(ipc, cret);
1217 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1218 filename, mimetype);
1225 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1227 void (*progress_gauge_callback)
1228 (CtdlIPC*, unsigned long, unsigned long),
1238 if (!cret) return -2;
1239 if (!buf) return -2;
1240 if (*buf) return -2;
1241 if (!part) return -2;
1242 if (!msgnum) return -2;
1243 if (ipc->downloading) return -2;
1245 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1246 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1247 if (ret / 100 == 2) {
1248 ipc->downloading = 1;
1249 bytes = extract_long(cret, 0);
1250 last_mod = extract_int(cret, 1);
1251 extract_token(filename, cret, 2, '|', sizeof filename);
1252 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1253 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1254 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, 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 CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1266 void (*progress_gauge_callback)
1267 (CtdlIPC*, unsigned long, unsigned long),
1276 if (!cret) return -1;
1277 if (!buf) return -1;
1278 if (*buf) return -1;
1279 if (!filename) return -1;
1280 if (ipc->downloading) return -1;
1282 aaa = (char *)malloc(strlen(filename) + 6);
1283 if (!aaa) return -1;
1285 sprintf(aaa, "OIMG %s", filename);
1286 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1288 if (ret / 100 == 2) {
1289 ipc->downloading = 1;
1290 bytes = extract_long(cret, 0);
1291 last_mod = extract_int(cret, 1);
1292 extract_token(mimetype, cret, 2, '|', 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 CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1307 void (*progress_gauge_callback)
1308 (CtdlIPC*, unsigned long, unsigned long),
1314 if (!cret) return -1;
1315 if (!save_as) return -1;
1316 if (!comment) return -1;
1317 if (!path) return -1;
1318 if (!*path) return -1;
1319 if (ipc->uploading) return -1;
1321 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1322 if (!aaa) return -1;
1324 sprintf(aaa, "UOPN %s|%s", save_as, comment);
1325 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1327 if (ret / 100 == 2) {
1329 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1330 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1338 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1339 const char *save_as,
1340 void (*progress_gauge_callback)
1341 (CtdlIPC*, unsigned long, unsigned long),
1347 if (!cret) return -1;
1348 if (!save_as) return -1;
1349 if (!path && for_real) return -1;
1350 if (!*path && for_real) return -1;
1351 if (ipc->uploading) return -1;
1353 aaa = (char *)malloc(strlen(save_as) + 17);
1354 if (!aaa) return -1;
1356 sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1357 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1359 if (ret / 100 == 2 && for_real) {
1361 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1362 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1370 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1375 if (!cret) return -2;
1376 if (!username) return -2;
1378 aaa = (char *)malloc(strlen(username) + 6);
1379 if (!aaa) return -1;
1381 sprintf(aaa, "QUSR %s", username);
1382 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1389 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1393 if (!cret) return -2;
1394 if (!listing) return -2;
1395 if (*listing) return -2;
1397 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1402 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1407 if (!cret) return -2;
1408 if (!name) return -2;
1410 sprintf(aaa, "CFLR %s|%d", name, for_real);
1411 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1417 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1421 if (!cret) return -1;
1422 if (floornum < 0) return -1;
1424 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1425 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1430 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1435 if (!cret) return -2;
1436 if (!floorname) return -2;
1437 if (floornum < 0) return -2;
1439 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1440 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1448 * You only need to fill out hostname, the defaults will be used if any of the
1449 * other fields are not set properly.
1451 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1452 int revision, const char *software_name, const char *hostname,
1458 if (developerid < 0 || clientid < 0 || revision < 0 ||
1462 revision = REV_LEVEL - 600;
1463 software_name = "Citadel (libcitadel)";
1465 if (!hostname) return -2;
1467 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1468 if (!aaa) return -1;
1470 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1471 revision, software_name, hostname);
1472 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1479 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1485 if (!cret) return -2;
1486 if (!username) return -2;
1488 aaa = (char *)malloc(strlen(username) + 8);
1489 if (!aaa) return -1;
1492 sprintf(aaa, "SEXP %s|-", username);
1493 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1496 sprintf(aaa, "SEXP %s||", username);
1497 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1505 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1509 if (!cret) return -2;
1510 if (!listing) return -2;
1511 if (*listing) return -2;
1513 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1518 /* mode is 0 = enable, 1 = disable, 2 = status */
1519 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1523 if (!cret) return -2;
1525 sprintf(aaa, "DEXP %d", mode);
1526 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1531 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1533 if (!cret) return -2;
1534 if (!bio) return -2;
1536 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1542 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1548 if (!cret) return -2;
1549 if (!username) return -2;
1550 if (!listing) return -2;
1551 if (*listing) return -2;
1553 aaa = (char *)malloc(strlen(username) + 6);
1554 if (!aaa) return -1;
1556 sprintf(aaa, "RBIO %s", username);
1557 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1564 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1568 if (!cret) return -2;
1569 if (!listing) return -2;
1570 if (*listing) return -2;
1572 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1577 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1581 if (!cret) return -1;
1583 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1584 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1589 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1593 if (!cret) return -1;
1595 sprintf(aaa, "TERM %d", sid);
1596 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1601 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1603 if (!cret) return -1;
1605 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1610 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1614 if (!cret) return -1;
1616 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1617 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1622 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1628 if (!cret) return -2;
1629 if (!text) return -2;
1630 if (!filename) return -2;
1632 aaa = (char *)malloc(strlen(filename) + 6);
1633 if (!aaa) return -1;
1635 sprintf(aaa, "EMSG %s", filename);
1636 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1643 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1648 if (!cret) return -2;
1649 if (!hostname) return -2;
1651 aaa = (char *)malloc(strlen(hostname) + 6);
1652 if (!aaa) return -1;
1654 sprintf(aaa, "HCHG %s", hostname);
1655 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1662 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1667 if (!cret) return -2;
1668 if (!roomname) return -2;
1670 aaa = (char *)malloc(strlen(roomname) + 6);
1671 if (!aaa) return -1;
1673 sprintf(aaa, "RCHG %s", roomname);
1674 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1681 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1686 if (!cret) return -2;
1687 if (!username) return -2;
1689 aaa = (char *)malloc(strlen(username) + 6);
1690 if (!aaa) return -1;
1692 sprintf(aaa, "UCHG %s", username);
1693 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1700 /* This function returns the actual server time reported, or 0 if error */
1701 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1703 register time_t tret;
1706 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1707 if (ret / 100 == 2) {
1708 tret = extract_long(cret, 0);
1717 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1718 struct ctdluser **uret, char *cret)
1723 if (!cret) return -2;
1724 if (!uret) return -2;
1725 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1726 if (!*uret) return -1;
1728 sprintf(aaa, "AGUP %s", who);
1729 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1731 if (ret / 100 == 2) {
1732 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1733 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1734 uret[0]->flags = extract_int(cret, 2);
1735 uret[0]->timescalled = extract_long(cret, 3);
1736 uret[0]->posted = extract_long(cret, 4);
1737 uret[0]->axlevel = extract_int(cret, 5);
1738 uret[0]->usernum = extract_long(cret, 6);
1739 uret[0]->lastcall = extract_long(cret, 7);
1740 uret[0]->USuserpurge = extract_int(cret, 8);
1747 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1752 if (!cret) return -2;
1753 if (!uret) return -2;
1755 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1756 if (!aaa) return -1;
1758 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1759 uret->fullname, uret->password, uret->flags,
1760 uret->timescalled, uret->posted, uret->axlevel,
1761 uret->usernum, uret->lastcall, uret->USuserpurge);
1762 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1769 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1770 /* caller must free the struct ExpirePolicy */
1771 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1772 struct ExpirePolicy **policy, char *cret)
1774 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1778 if (!cret) return -2;
1779 if (!policy) return -2;
1780 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1781 if (!*policy) return -1;
1782 if (which < 0 || which > 3) return -2;
1784 sprintf(cmd, "GPEX %s", proto[which]);
1785 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1786 if (ret / 100 == 2) {
1787 policy[0]->expire_mode = extract_int(cret, 0);
1788 policy[0]->expire_value = extract_int(cret, 1);
1795 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1796 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1797 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1798 struct ExpirePolicy *policy, char *cret)
1801 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1803 if (!cret) return -2;
1804 if (which < 0 || which > 3) return -2;
1805 if (!policy) return -2;
1806 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1807 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1809 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1810 policy->expire_mode, policy->expire_value);
1811 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1816 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1820 if (!cret) return -2;
1821 if (!listing) return -2;
1822 if (*listing) return -2;
1824 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1825 listing, &bytes, cret);
1830 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1832 if (!cret) return -2;
1833 if (!listing) return -2;
1835 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1841 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1842 char **listing, char *cret)
1848 if (!cret) return -2;
1849 if (!mimetype) return -2;
1850 if (!listing) return -2;
1851 if (*listing) return -2;
1853 aaa = malloc(strlen(mimetype) + 13);
1854 if (!aaa) return -1;
1855 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1856 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1857 listing, &bytes, cret);
1864 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1865 const char *listing, char *cret)
1870 if (!cret) return -2;
1871 if (!mimetype) return -2;
1872 if (!listing) return -2;
1874 aaa = malloc(strlen(mimetype) + 13);
1875 if (!aaa) return -1;
1876 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1877 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1885 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1889 if (!cret) return -2;
1890 if (!listing) return -2;
1891 if (*listing) return -2;
1893 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1894 listing, &bytes, cret);
1899 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1901 if (!cret) return -2;
1902 if (!listing) return -2;
1904 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1910 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1914 if (!cret) return -2;
1915 if (session < 0) return -2;
1917 sprintf(aaa, "REQT %d", session);
1918 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1923 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1927 if (!cret) return -2;
1928 if (msgnum < 0) return -2;
1930 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1931 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1936 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1945 /* New SSL object */
1946 temp_ssl = SSL_new(ssl_ctx);
1948 error_printf("SSL_new failed: %s\n",
1949 ERR_reason_error_string(ERR_get_error()));
1952 /* Pointless flag waving */
1953 #if SSLEAY_VERSION_NUMBER >= 0x0922
1954 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1957 if (!access(EGD_POOL, F_OK))
1960 if (!RAND_status()) {
1961 error_printf("PRNG not properly seeded\n");
1965 /* Associate network connection with SSL object */
1966 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1967 error_printf("SSL_set_fd failed: %s\n",
1968 ERR_reason_error_string(ERR_get_error()));
1972 if (status_hook != NULL)
1973 status_hook("Requesting encryption...\r");
1975 /* Ready to start SSL/TLS */
1977 CtdlIPC_putline(ipc, "STLS");
1978 CtdlIPC_getline(ipc, buf);
1979 if (buf[0] != '2') {
1980 error_printf("Server can't start TLS: %s\n", buf);
1984 r = CtdlIPCGenericCommand(ipc,
1985 "STLS", NULL, 0, NULL, NULL, cret);
1987 error_printf("Server can't start TLS: %s\n", buf);
1992 /* Do SSL/TLS handshake */
1993 if ((a = SSL_connect(temp_ssl)) < 1) {
1994 error_printf("SSL_connect failed: %s\n",
1995 ERR_reason_error_string(ERR_get_error()));
1999 ipc->ssl = temp_ssl;
2001 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
2005 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
2006 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2007 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2008 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2014 #endif /* HAVE_OPENSSL */
2019 static void endtls(SSL *ssl)
2030 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2035 if (!address) return -2;
2036 if (!cret) return -2;
2038 aaa = (char *)malloc(strlen(address) + 6);
2039 if (!aaa) return -1;
2041 sprintf(aaa, "QDIR %s", address);
2042 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2049 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2053 if (!cret) return -2;
2054 sprintf(aaa, "IPGM %d", secret);
2055 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2060 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2064 if (!cret) return -2;
2065 if (!mret) return -2;
2066 if (*mret) return -2;
2068 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2089 /* ************************************************************************** */
2090 /* Stuff below this line is not for public consumption */
2091 /* ************************************************************************** */
2094 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2096 if (ipc->network_status_cb) ipc->network_status_cb(1);
2097 #ifdef THREADED_CLIENT
2098 pthread_mutex_lock(&(ipc->mutex));
2103 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2105 #ifdef THREADED_CLIENT
2106 pthread_mutex_unlock(&(ipc->mutex));
2108 if (ipc->network_status_cb) ipc->network_status_cb(0);
2112 /* Read a listing from the server up to 000. Append to dest if it exists */
2113 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2122 length = strlen(ret);
2127 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2128 linelength = strlen(aaa);
2129 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2131 strcpy(&ret[length], aaa);
2132 length += linelength;
2133 strcpy(&ret[length++], "\n");
2141 /* Send a listing to the server; generate the ending 000. */
2142 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2146 text = (char *)malloc(strlen(listing) + 6);
2148 strcpy(text, listing);
2149 while (text[strlen(text) - 1] == '\n')
2150 text[strlen(text) - 1] = '\0';
2151 strcat(text, "\n000");
2152 CtdlIPC_putline(ipc, text);
2156 /* Malloc failed but we are committed to send */
2157 /* This may result in extra blanks at the bottom */
2158 CtdlIPC_putline(ipc, text);
2159 CtdlIPC_putline(ipc, "000");
2165 /* Partial read of file from server */
2166 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2168 register size_t len = 0;
2172 if (!cret) return 0;
2173 if (bytes < 1) return 0;
2176 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2177 CtdlIPC_putline(ipc, aaa);
2178 CtdlIPC_getline(ipc, aaa);
2180 strcpy(cret, &aaa[4]);
2182 len = extract_long(&aaa[4], 0);
2183 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2185 /* I know what I'm doing */
2186 serv_read(ipc, ((char *)(*buf) + offset), len);
2188 /* We have to read regardless */
2189 serv_read(ipc, aaa, len);
2193 CtdlIPC_unlock(ipc);
2199 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2203 if (!cret) return -2;
2204 if (!ipc->downloading) return -2;
2206 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2208 ipc->downloading = 0;
2214 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2218 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2219 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2226 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2227 void (*progress_gauge_callback)
2228 (CtdlIPC*, unsigned long, unsigned long),
2231 register size_t len;
2233 if (!cret) return -1;
2234 if (!buf) return -1;
2235 if (*buf) return -1;
2236 if (!ipc->downloading) return -1;
2239 if (progress_gauge_callback)
2240 progress_gauge_callback(ipc, len, bytes);
2241 while (len < bytes) {
2242 register size_t block;
2244 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2250 if (progress_gauge_callback)
2251 progress_gauge_callback(ipc, len, bytes);
2256 /* READ - pipelined */
2257 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2259 void (*progress_gauge_callback)
2260 (CtdlIPC*, unsigned long, unsigned long),
2263 register size_t len;
2264 register int calls; /* How many calls in the pipeline */
2265 register int i; /* iterator */
2268 if (!cret) return -1;
2269 if (!buf) return -1;
2270 if (*buf) return -1;
2271 if (!ipc->downloading) return -1;
2273 *buf = (void *)realloc(*buf, bytes - resume);
2274 if (!*buf) return -1;
2278 if (progress_gauge_callback)
2279 progress_gauge_callback(ipc, len, bytes);
2281 /* How many calls will be in the pipeline? */
2282 calls = (bytes - resume) / 4096;
2283 if ((bytes - resume) % 4096) calls++;
2285 /* Send all requests at once */
2286 for (i = 0; i < calls; i++) {
2287 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2288 CtdlIPC_putline(ipc, aaa);
2291 /* Receive all responses at once */
2292 for (i = 0; i < calls; i++) {
2293 CtdlIPC_getline(ipc, aaa);
2295 strcpy(cret, &aaa[4]);
2297 len = extract_long(&aaa[4], 0);
2298 /* I know what I'm doing */
2299 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2301 if (progress_gauge_callback)
2302 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2304 CtdlIPC_unlock(ipc);
2310 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2315 if (!cret) return -1;
2316 if (!ipc->uploading) return -1;
2318 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2319 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2326 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2327 void (*progress_gauge_callback)
2328 (CtdlIPC*, unsigned long, unsigned long),
2331 register int ret = -1;
2332 register size_t offset = 0;
2339 if (!cret) return -1;
2340 if (!path) return -1;
2341 if (!*path) return -1;
2343 fd = fopen(path, "r");
2346 fseek(fd, 0L, SEEK_END);
2350 if (progress_gauge_callback)
2351 progress_gauge_callback(ipc, 0, bytes);
2353 while (offset < bytes) {
2354 register size_t to_write;
2356 /* Read some data in */
2357 to_write = fread(buf, 1, 4096, fd);
2359 if (feof(fd) || ferror(fd)) break;
2361 sprintf(aaa, "WRIT %d", (int)to_write);
2362 CtdlIPC_putline(ipc, aaa);
2363 CtdlIPC_getline(ipc, aaa);
2364 strcpy(cret, &aaa[4]);
2366 if (aaa[0] == '7') {
2367 to_write = extract_long(&aaa[4], 0);
2369 serv_write(ipc, buf, to_write);
2371 if (progress_gauge_callback)
2372 progress_gauge_callback(ipc, offset, bytes);
2373 /* Detect short reads and back up if needed */
2374 /* offset will never be negative anyway */
2375 fseek(fd, (signed)offset, SEEK_SET);
2380 if (progress_gauge_callback)
2381 progress_gauge_callback(ipc, 1, 1);
2384 return (!ferr ? ret : -2);
2389 * Generic command method. This method should handle any server command
2390 * except for CHAT. It takes the following arguments:
2392 * ipc The server to speak with
2393 * command Preformatted command to send to server
2394 * to_send A text or binary file to send to server
2395 * (only sent if server requests it)
2396 * bytes_to_send The number of bytes in to_send (required if
2397 * sending binary, optional if sending listing)
2398 * to_receive Pointer to a NULL pointer, if the server
2399 * sends text or binary we will allocate memory
2400 * for the file and stuff it here
2401 * bytes_to_receive If a file is received, we will store its
2403 * proto_response The protocol response. Caller must provide
2404 * this buffer and ensure that it is at least
2405 * 128 bytes in length.
2407 * This function returns a number equal to the protocol response number,
2408 * -1 if an internal error occurred, -2 if caller provided bad values,
2409 * or 0 - the protocol response number if bad values were found during
2410 * the protocol exchange.
2411 * It stores the protocol response string (minus the number) in
2412 * protocol_response as described above. Some commands send additional
2413 * data in this string.
2415 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2416 const char *command, const char *to_send,
2417 size_t bytes_to_send, char **to_receive,
2418 size_t *bytes_to_receive, char *proto_response)
2424 if (!command) return -2;
2425 if (!proto_response) return -2;
2428 if (ipc->ssl) watch_ssl = 1;
2432 CtdlIPC_putline(ipc, command);
2434 CtdlIPC_getline(ipc, proto_response);
2435 if (proto_response[3] == '*')
2437 ret = atoi(proto_response);
2438 strcpy(proto_response, &proto_response[4]);
2439 switch (ret / 100) {
2440 default: /* Unknown, punt */
2442 case 3: /* MORE_DATA */
2444 /* Don't need to do anything */
2446 case 1: /* LISTING_FOLLOWS */
2447 if (to_receive && !*to_receive && bytes_to_receive) {
2448 *to_receive = CtdlIPCReadListing(ipc, NULL);
2449 } else { /* Drain */
2450 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2454 case 4: /* SEND_LISTING */
2456 CtdlIPCSendListing(ipc, to_send);
2458 /* No listing given, fake it */
2459 CtdlIPC_putline(ipc, "000");
2463 case 6: /* BINARY_FOLLOWS */
2464 if (to_receive && !*to_receive && bytes_to_receive) {
2466 extract_long(proto_response, 0);
2467 *to_receive = (char *)
2468 malloc((size_t)*bytes_to_receive);
2472 serv_read(ipc, *to_receive,
2479 drain = extract_long(proto_response, 0);
2480 while (drain > SIZ) {
2481 serv_read(ipc, buf, SIZ);
2484 serv_read(ipc, buf, drain);
2488 case 7: /* SEND_BINARY */
2489 if (to_send && bytes_to_send) {
2490 serv_write(ipc, to_send, bytes_to_send);
2491 } else if (bytes_to_send) {
2492 /* Fake it, send nulls */
2495 fake = bytes_to_send;
2496 memset(buf, '\0', SIZ);
2497 while (fake > SIZ) {
2498 serv_write(ipc, buf, SIZ);
2501 serv_write(ipc, buf, fake);
2503 } /* else who knows? DANGER WILL ROBINSON */
2505 case 8: /* START_CHAT_MODE */
2506 if (!strncasecmp(command, "CHAT", 4)) {
2507 /* Don't call chatmode with generic! */
2508 CtdlIPC_putline(ipc, "/quit");
2511 /* In this mode we send then receive listing */
2513 CtdlIPCSendListing(ipc, to_send);
2515 /* No listing given, fake it */
2516 CtdlIPC_putline(ipc, "000");
2519 if (to_receive && !*to_receive
2520 && bytes_to_receive) {
2521 *to_receive = CtdlIPCReadListing(ipc, NULL);
2522 } else { /* Drain */
2523 while (CtdlIPC_getline(ipc, buf),
2524 strcmp(buf, "000")) ;
2529 case 9: /* ASYNC_MSG */
2530 /* CtdlIPCDoAsync(ret, proto_response); */
2531 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2537 CtdlIPC_unlock(ipc);
2542 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2544 struct hostent *phe;
2545 struct servent *pse;
2546 struct protoent *ppe;
2547 struct sockaddr_in sin;
2550 memset(&sin, 0, sizeof(sin));
2551 sin.sin_family = AF_INET;
2553 pse = getservbyname(service, protocol);
2555 sin.sin_port = pse->s_port;
2557 else if (atoi(service) > 0) {
2558 sin.sin_port = htons(atoi(service));
2561 sin.sin_port = htons(defaultPort);
2563 phe = gethostbyname(host);
2565 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2566 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2569 if ((ppe = getprotobyname(protocol)) == 0) {
2572 if (!strcmp(protocol, "udp")) {
2578 s = socket(PF_INET, type, ppe->p_proto);
2583 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2591 static int uds_connectsock(int *isLocal, char *sockpath)
2593 struct sockaddr_un addr;
2596 memset(&addr, 0, sizeof(addr));
2597 addr.sun_family = AF_UNIX;
2598 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2600 s = socket(AF_UNIX, SOCK_STREAM, 0);
2605 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2616 * input binary data from socket
2618 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2620 unsigned int len, rlen;
2622 #if defined(HAVE_OPENSSL)
2624 serv_read_ssl(ipc, buf, bytes);
2629 while (len < bytes) {
2630 rlen = read(ipc->sock, &buf[len], bytes - len);
2632 connection_died(ipc, 0);
2641 * send binary to server
2643 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2645 unsigned int bytes_written = 0;
2648 #if defined(HAVE_OPENSSL)
2650 serv_write_ssl(ipc, buf, nbytes);
2654 while (bytes_written < nbytes) {
2655 retval = write(ipc->sock, &buf[bytes_written],
2656 nbytes - bytes_written);
2658 connection_died(ipc, 0);
2661 bytes_written += retval;
2668 * input binary data from encrypted connection
2670 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2676 while (len < bytes) {
2677 if (SSL_want_read(ipc->ssl)) {
2678 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2679 error_printf("SSL_write in serv_read:\n");
2680 ERR_print_errors_fp(stderr);
2683 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2687 errval = SSL_get_error(ipc->ssl, rlen);
2688 if (errval == SSL_ERROR_WANT_READ ||
2689 errval == SSL_ERROR_WANT_WRITE) {
2694 Not sure why we'd want to handle these error codes any differently,
2695 but this definitely isn't the way to handle them. Someone must have
2696 naively assumed that we could fall back to unencrypted communications,
2697 but all it does is just recursively blow the stack.
2698 if (errval == SSL_ERROR_ZERO_RETURN ||
2699 errval == SSL_ERROR_SSL) {
2700 serv_read(ipc, &buf[len], bytes - len);
2704 error_printf("SSL_read in serv_read: %s\n",
2705 ERR_reason_error_string(ERR_peek_error()));
2706 connection_died(ipc, 1);
2715 * send binary to server encrypted
2717 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2719 unsigned int bytes_written = 0;
2723 while (bytes_written < nbytes) {
2724 if (SSL_want_write(ipc->ssl)) {
2725 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2726 error_printf("SSL_read in serv_write:\n");
2727 ERR_print_errors_fp(stderr);
2730 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2731 nbytes - bytes_written);
2735 errval = SSL_get_error(ipc->ssl, retval);
2736 if (errval == SSL_ERROR_WANT_READ ||
2737 errval == SSL_ERROR_WANT_WRITE) {
2741 if (errval == SSL_ERROR_ZERO_RETURN ||
2742 errval == SSL_ERROR_SSL) {
2743 serv_write(ipc, &buf[bytes_written],
2744 nbytes - bytes_written);
2747 error_printf("SSL_write in serv_write: %s\n",
2748 ERR_reason_error_string(ERR_peek_error()));
2749 connection_died(ipc, 1);
2752 bytes_written += retval;
2757 #ifdef THREADED_CLIENT
2758 static void ssl_lock(int mode, int n, const char *file, int line)
2760 if (mode & CRYPTO_LOCK)
2761 pthread_mutex_lock(Critters[n]);
2763 pthread_mutex_unlock(Critters[n]);
2765 #endif /* THREADED_CLIENT */
2768 static void CtdlIPC_init_OpenSSL(void)
2771 SSL_METHOD *ssl_method;
2774 /* already done init */
2783 SSL_load_error_strings();
2784 SSLeay_add_ssl_algorithms();
2786 /* Set up the SSL context in which we will oeprate */
2787 ssl_method = SSLv23_client_method();
2788 ssl_ctx = SSL_CTX_new(ssl_method);
2790 error_printf("SSL_CTX_new failed: %s\n",
2791 ERR_reason_error_string(ERR_get_error()));
2794 /* Any reasonable cipher we can get */
2795 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2796 error_printf("No ciphers available for encryption\n");
2799 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2801 /* Load DH parameters into the context */
2804 error_printf("Can't allocate a DH object: %s\n",
2805 ERR_reason_error_string(ERR_get_error()));
2808 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2809 error_printf("Can't assign DH_P: %s\n",
2810 ERR_reason_error_string(ERR_get_error()));
2814 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2815 error_printf("Can't assign DH_G: %s\n",
2816 ERR_reason_error_string(ERR_get_error()));
2821 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2824 #ifdef THREADED_CLIENT
2825 /* OpenSSL requires callbacks for threaded clients */
2826 CRYPTO_set_locking_callback(ssl_lock);
2827 CRYPTO_set_id_callback(id_callback);
2829 /* OpenSSL requires us to do semaphores for threaded clients */
2830 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2832 perror("malloc failed");
2835 for (a = 0; a < CRYPTO_num_locks(); a++) {
2836 Critters[a] = malloc(sizeof (pthread_mutex_t));
2838 perror("malloc failed");
2841 pthread_mutex_init(Critters[a], NULL);
2844 #endif /* THREADED_CLIENT */
2849 #ifdef THREADED_CLIENT
2850 static unsigned long id_callback(void) {
2851 return (unsigned long)pthread_self();
2853 #endif /* THREADED_CLIENT */
2854 #endif /* HAVE_OPENSSL */
2858 ReadNetworkChunk(CtdlIPC* ipc)
2869 FD_SET(ipc->sock, &read_fd);
2870 ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
2872 if (!(errno == EINTR || errno == EAGAIN))
2873 fprintf(stderr, "\nselect failed: %d %s\n", err, strerror(err));
2880 *(ipc->BufPtr) = '\0';
2881 n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
2883 ipc->BufPtr[n]='\0';
2892 * input string from socket - implemented in terms of serv_read()
2894 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2897 char *aptr, *bptr, *aeptr, *beptr;
2900 #if defined(HAVE_OPENSSL)
2903 /* Read one character at a time. */
2905 serv_read(ipc, &buf[i], 1);
2906 if (buf[i] == '\n' || i == (SIZ-1))
2910 /* If we got a long line, discard characters until the newline. */
2912 while (buf[i] != '\n')
2913 serv_read(ipc, &buf[i], 1);
2915 /* Strip the trailing newline (and carriage return, if present) */
2916 if (i>=0 && buf[i] == 10) buf[i--] = 0;
2917 if (i>=0 && buf[i] == 13) buf[i--] = 0;
2922 if (ipc->Buf == NULL)
2925 ipc->Buf = (char*) malloc(ipc->BufSize + 10);
2927 ipc->BufPtr = ipc->Buf;
2930 if (ipc->BufUsed == 0)
2931 while ( ReadNetworkChunk(ipc) < 0 )
2939 aeptr = ipc->Buf + ipc->BufSize;
2940 while ((aptr < aeptr) &&
2944 *(bptr++) = *(aptr++);
2945 if ((*aptr == '\n') && (aptr < aeptr))
2947 /* Terminate it right, remove the line breaks */
2948 while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
2950 while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
2953 // fprintf(stderr, "parsing %d %d %d - %d %d %d\n", ipc->BufPtr - ipc->Buf, aptr - ipc->BufPtr, ipc->BufUsed , *aptr, *(aptr-1), *(aptr+1));
2954 if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
2957 /* is there more in the buffer we need to read later? */
2958 if (ipc->Buf + ipc->BufUsed > aptr)
2965 ipc->BufPtr = ipc->Buf;
2969 }/* should we move our read stuf to the bufferstart so we have more space at the end? */
2970 else if ((ipc->BufPtr != ipc->Buf) &&
2971 (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
2973 size_t NewBufSize = ipc->BufSize * 2;
2974 int delta = (ipc->BufPtr - ipc->Buf);
2977 /* if the line would end after our buffer, we should use a bigger buffer. */
2978 NewBuf = (char *)malloc (NewBufSize + 10);
2979 memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
2981 ipc->Buf = ipc->BufPtr = NewBuf;
2982 ipc->BufUsed -= delta;
2983 ipc->BufSize = NewBufSize;
2987 if (ReadNetworkChunk(ipc) <0)
2993 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2995 CtdlIPC_getline(ipc, buf);
2999 * send line to server - implemented in terms of serv_write()
3001 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
3007 cmd = malloc(len + 2);
3009 /* This requires no extra memory */
3010 serv_write(ipc, buf, len);
3011 serv_write(ipc, "\n", 1);
3013 /* This is network-optimized */
3014 strncpy(cmd, buf, len);
3015 strcpy(cmd + len, "\n");
3016 serv_write(ipc, cmd, len + 1);
3020 ipc->last_command_sent = time(NULL);
3023 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
3025 CtdlIPC_putline(ipc, buf);
3032 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
3040 ipc = ialloc(CtdlIPC);
3044 #if defined(HAVE_OPENSSL)
3046 CtdlIPC_init_OpenSSL();
3048 #if defined(HAVE_PTHREAD_H)
3049 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
3051 ipc->sock = -1; /* Not connected */
3052 ipc->isLocal = 0; /* Not local, of course! */
3053 ipc->downloading = 0;
3055 ipc->last_command_sent = 0L;
3056 ipc->network_status_cb = NULL;
3061 strcpy(cithost, DEFAULT_HOST); /* default host */
3062 strcpy(citport, DEFAULT_PORT); /* default port */
3064 /* Allow caller to supply our values (Windows) */
3065 if (hostbuf && strlen(hostbuf) > 0)
3066 strcpy(cithost, hostbuf);
3067 if (portbuf && strlen(portbuf) > 0)
3068 strcpy(citport, portbuf);
3070 /* Read host/port from command line if present */
3071 for (a = 0; a < argc; ++a) {
3074 } else if (a == 1) {
3075 strcpy(cithost, argv[a]);
3076 } else if (a == 2) {
3077 strcpy(citport, argv[a]);
3079 error_printf("%s: usage: ",argv[0]);
3080 error_printf("%s [host] [port] ",argv[0]);
3087 if ((!strcmp(cithost, "localhost"))
3088 || (!strcmp(cithost, "127.0.0.1"))) {
3092 /* If we're using a unix domain socket we can do a bunch of stuff */
3093 if (!strcmp(cithost, UDS)) {
3094 if (!strcasecmp(citport, DEFAULT_PORT)) {
3095 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
3098 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
3100 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
3101 if (ipc->sock == -1) {
3105 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3106 if (portbuf != NULL) strcpy(portbuf, sockpath);
3110 ipc->sock = connectsock(cithost, citport, "tcp", 504);
3111 if (ipc->sock == -1) {
3115 if (hostbuf != NULL) strcpy(hostbuf, cithost);
3116 if (portbuf != NULL) strcpy(portbuf, citport);
3122 * Disconnect and delete the IPC class (destructor)
3124 void CtdlIPC_delete(CtdlIPC* ipc)
3128 SSL_shutdown(ipc->ssl);
3133 if (ipc->sock > -1) {
3134 shutdown(ipc->sock, 2); /* Close it up */
3137 if (ipc->Buf != NULL)
3146 * Disconnect and delete the IPC class (destructor)
3147 * Also NULLs out the pointer
3149 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3151 CtdlIPC_delete(*pipc);
3157 * return the file descriptor of the server socket so we can select() on it.
3159 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3162 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3169 * return one character
3171 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3174 char CtdlIPC_get(CtdlIPC* ipc)
3179 serv_read(ipc, buf, 1);