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 ssl_lock(int mode, int n, const char *file, int line);
75 static void endtls(SSL *ssl);
76 #ifdef THREADED_CLIENT
77 static unsigned long id_callback(void);
78 #endif /* THREADED_CLIENT */
79 #endif /* HAVE_OPENSSL */
80 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
81 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
85 * Does nothing. The server should always return 200.
87 int CtdlIPCNoop(CtdlIPC *ipc)
91 return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
96 * Does nothing interesting. The server should always return 200
97 * along with your string.
99 int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
105 if (!cret) return -2;
107 aaa = (char *)malloc((size_t)(strlen(arg) + 6));
110 sprintf(aaa, "ECHO %s", arg);
111 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
118 * Asks the server to close the connecction.
119 * Should always return 200.
121 int CtdlIPCQuit(CtdlIPC *ipc)
123 register int ret = 221; /* Default to successful quit */
127 if (ipc->sock > -1) {
128 CtdlIPC_putline(ipc, "QUIT");
129 CtdlIPC_getline(ipc, aaa);
134 SSL_shutdown(ipc->ssl);
138 shutdown(ipc->sock, 2); /* Close connection; we're dead */
146 * Asks the server to logout. Should always return 200, even if no user
147 * was logged in. The user will not be logged in after this!
149 int CtdlIPCLogout(CtdlIPC *ipc)
155 CtdlIPC_putline(ipc, "LOUT");
156 CtdlIPC_getline(ipc, aaa);
164 * First stage of authentication - pass the username. Returns 300 if the
165 * username is able to log in, with the username correctly spelled in cret.
166 * Returns various 500 error codes if the user doesn't exist, etc.
168 int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
173 if (!username) return -2;
174 if (!cret) return -2;
176 aaa = (char *)malloc((size_t)(strlen(username) + 6));
179 sprintf(aaa, "USER %s", username);
180 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
187 * Second stage of authentication - provide password. The server returns
188 * 200 and several arguments in cret relating to the user's account.
190 int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
195 if (!passwd) return -2;
196 if (!cret) return -2;
198 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
201 sprintf(aaa, "PASS %s", passwd);
202 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
209 * Second stage of authentication - provide password. The server returns
210 * 200 and several arguments in cret relating to the user's account.
212 int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
217 if (!response) return -2;
218 if (!cret) return -2;
220 aaa = (char *)malloc((size_t)(strlen(response) + 6));
223 sprintf(aaa, "PAS2 %s", response);
224 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
231 * Create a new user. This returns 200 plus the same arguments as TryPassword
232 * if selfservice is nonzero, unless there was a problem creating the account.
233 * If selfservice is zero, creates a new user but does not log out the existing
234 * user - intended for use by system administrators to create accounts on
235 * behalf of other users.
237 int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
242 if (!username) return -2;
243 if (!cret) return -2;
245 aaa = (char *)malloc((size_t)(strlen(username) + 6));
248 sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
249 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
256 * Changes the user's password. Returns 200 if changed, errors otherwise.
258 int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
263 if (!passwd) return -2;
264 if (!cret) return -2;
266 aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
269 sprintf(aaa, "SETP %s", passwd);
270 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
277 /* Caller must free the march list */
278 /* Room types are defined in enum RoomList; keep these in sync! */
279 /* floor is -1 for all, or floornum */
280 int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
283 struct march *march = NULL;
284 static char *proto[] =
285 {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
290 if (!listing) return -2;
291 if (*listing) return -2; /* Free the listing first */
292 if (!cret) return -2;
293 /* if (which < 0 || which > 4) return -2; */
294 if (floor < -1) return -2; /* Can't validate upper bound, sorry */
296 sprintf(aaa, "%s %d", proto[which], floor);
297 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
298 if (ret / 100 == 1) {
301 while (bbb && strlen(bbb)) {
304 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
306 memmove(bbb, bbb + a + 1, strlen(bbb) - a);
307 mptr = (struct march *) malloc(sizeof (struct march));
310 extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
311 mptr->march_flags = (unsigned int) extract_int(aaa, 1);
312 mptr->march_floor = (char) extract_int(aaa, 2);
313 mptr->march_order = (char) extract_int(aaa, 3);
314 mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
315 mptr->march_access = (char) extract_int(aaa, 5);
322 while (mptr2->next != NULL)
336 /* Caller must free the struct ctdluser; caller may pass an existing one */
337 int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
341 if (!cret) return -2;
342 if (!uret) return -2;
343 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
344 if (!*uret) return -1;
346 ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
347 if (ret / 100 == 2) {
348 uret[0]->USscreenwidth = extract_int(cret, 0);
349 uret[0]->USscreenheight = extract_int(cret, 1);
350 uret[0]->flags = extract_int(cret, 2);
357 int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
361 if (!uret) return -2;
362 if (!cret) return -2;
364 sprintf(aaa, "SETU %d|%d|%d",
365 uret->USscreenwidth, uret->USscreenheight,
367 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
372 int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
373 struct ctdlipcroom **rret, char *cret)
378 if (!cret) return -2;
379 if (!rret) return -2;
380 if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
381 if (!*rret) return -1;
384 aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
389 sprintf(aaa, "GOTO %s|%s", room, passwd);
391 aaa = (char *)malloc(strlen(room) + 6);
396 sprintf(aaa, "GOTO %s", room);
398 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
399 if (ret / 100 == 2) {
400 extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
401 rret[0]->RRunread = extract_long(cret, 1);
402 rret[0]->RRtotal = extract_long(cret, 2);
403 rret[0]->RRinfoupdated = extract_int(cret, 3);
404 rret[0]->RRflags = extract_int(cret, 4);
405 rret[0]->RRhighest = extract_long(cret, 5);
406 rret[0]->RRlastread = extract_long(cret, 6);
407 rret[0]->RRismailbox = extract_int(cret, 7);
408 rret[0]->RRaide = extract_int(cret, 8);
409 rret[0]->RRnewmail = extract_long(cret, 9);
410 rret[0]->RRfloor = extract_int(cret, 10);
411 rret[0]->RRflags2 = extract_int(cret, 14);
422 /* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
423 /* whicharg is number of messages, applies to last, first, gt, lt */
424 int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
425 const char *mtemplate, unsigned long **mret, char *cret)
428 register unsigned long count = 0;
429 static char *proto[] =
430 { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
435 if (!cret) return -2;
436 if (!mret) return -2;
437 if (*mret) return -2;
438 if (which < 0 || which > 6) return -2;
441 sprintf(aaa, "MSGS %s||%d", proto[which],
442 (mtemplate) ? 1 : 0);
444 sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
445 (mtemplate) ? 1 : 0);
446 if (mtemplate) count = strlen(mtemplate);
447 ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
451 *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
454 while (bbb && strlen(bbb)) {
455 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
456 remove_token(bbb, 0, '\n');
457 *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
458 sizeof (unsigned long)));
460 (*mret)[count++] = atol(aaa);
472 int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
473 struct ctdlipcmessage **mret, char *cret)
479 int multipart_hunting = 0;
480 char multipart_prefix[128];
482 if (!cret) return -1;
483 if (!mret) return -1;
484 if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
485 if (!*mret) return -1;
486 if (!msgnum) return -1;
488 strcpy(mret[0]->content_type, "");
489 sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
490 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
491 if (ret / 100 == 1) {
493 strcpy(mret[0]->mime_chosen, "1"); /* Default chosen-part is "1" */
494 while (strlen(bbb) > 4 && bbb[4] == '=') {
495 extract_token(aaa, bbb, 0, '\n', sizeof aaa);
496 remove_token(bbb, 0, '\n');
498 if (!strncasecmp(aaa, "nhdr=yes", 8))
500 else if (!strncasecmp(aaa, "from=", 5))
501 safestrncpy(mret[0]->author, &aaa[5], SIZ);
502 else if (!strncasecmp(aaa, "type=", 5))
503 mret[0]->type = atoi(&aaa[5]);
504 else if (!strncasecmp(aaa, "msgn=", 5))
505 safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
506 else if (!strncasecmp(aaa, "subj=", 5))
507 safestrncpy(mret[0]->subject, &aaa[5], SIZ);
508 else if (!strncasecmp(aaa, "rfca=", 5))
509 safestrncpy(mret[0]->email, &aaa[5], SIZ);
510 else if (!strncasecmp(aaa, "hnod=", 5))
511 safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
512 else if (!strncasecmp(aaa, "room=", 5))
513 safestrncpy(mret[0]->room, &aaa[5], SIZ);
514 else if (!strncasecmp(aaa, "node=", 5))
515 safestrncpy(mret[0]->node, &aaa[5], SIZ);
516 else if (!strncasecmp(aaa, "rcpt=", 5))
517 safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
518 else if (!strncasecmp(aaa, "time=", 5))
519 mret[0]->time = atol(&aaa[5]);
521 /* Multipart/alternative prefix & suffix strings help
522 * us to determine which part we want to download.
524 else if (!strncasecmp(aaa, "pref=", 5)) {
525 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
526 if (!strcasecmp(multipart_prefix,
527 "multipart/alternative")) {
531 else if (!strncasecmp(aaa, "suff=", 5)) {
532 extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
533 if (!strcasecmp(multipart_prefix,
534 "multipart/alternative")) {
539 else if (!strncasecmp(aaa, "part=", 5)) {
540 struct parts *ptr, *chain;
542 ptr = (struct parts *)calloc(1, sizeof (struct parts));
545 /* Fill the buffers for the caller */
546 extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
547 extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
548 extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
549 extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
550 extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
551 ptr->length = extract_long(&aaa[5], 5);
552 if (!mret[0]->attachments)
553 mret[0]->attachments = ptr;
555 chain = mret[0]->attachments;
561 /* Now handle multipart/alternative */
562 if (multipart_hunting > 0) {
563 if ( (!strcasecmp(ptr->mimetype,
565 || (!strcasecmp(ptr->mimetype,
567 strcpy(mret[0]->mime_chosen,
575 /* Eliminate "text\n" */
576 remove_token(bbb, 0, '\n');
578 /* If doing a MIME thing, pull out the extra headers */
581 if (!strncasecmp(bbb, "Content-type:", 13)) {
582 extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
583 strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
584 striplt(mret[0]->content_type);
586 /* strip out ";charset=" portion. FIXME do something with
587 * the charset (like... convert it) instead of just throwing
590 if (strstr(mret[0]->content_type, ";") != NULL) {
591 strcpy(strstr(mret[0]->content_type, ";"), "");
595 if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
596 extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
597 strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
598 striplt(mret[0]->mime_chosen);
600 remove_token(bbb, 0, '\n');
601 } while ((bbb[0] != 0) && (bbb[0] != '\n'));
602 remove_token(bbb, 0, '\n');
608 /* FIXME: Strip trailing whitespace */
609 bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
611 bbb = (char *)realloc(bbb, 1);
621 int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
626 if (!cret) return -2;
627 if (!listing) return -2;
628 if (*listing) return -2;
630 ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
636 int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
640 char *listing = NULL;
643 if (!cret) return -2;
645 ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
646 if (ret / 100 == 1) {
649 while (*listing && strlen(listing)) {
650 extract_token(buf, listing, 0, '\n', sizeof buf);
651 remove_token(listing, 0, '\n');
653 case 0: ipc->ServInfo.pid = atoi(buf);
655 case 1: strcpy(ipc->ServInfo.nodename,buf);
657 case 2: strcpy(ipc->ServInfo.humannode,buf);
659 case 3: strcpy(ipc->ServInfo.fqdn,buf);
661 case 4: strcpy(ipc->ServInfo.software,buf);
663 case 5: ipc->ServInfo.rev_level = atoi(buf);
665 case 6: strcpy(ipc->ServInfo.site_location,buf);
667 case 7: strcpy(ipc->ServInfo.sysadm,buf);
669 case 9: strcpy(ipc->ServInfo.moreprompt,buf);
671 case 10: ipc->ServInfo.ok_floors = atoi(buf);
673 case 11: ipc->ServInfo.paging_level = atoi(buf);
675 case 13: ipc->ServInfo.supports_qnop = atoi(buf);
677 case 14: ipc->ServInfo.supports_ldap = atoi(buf);
679 case 15: ipc->ServInfo.newuser_disabled = atoi(buf);
685 if (listing) free(listing);
691 int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
696 if (!cret) return -2;
697 if (!listing) return -2;
698 if (*listing) return -2;
700 ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
706 * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
708 int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
713 if (!cret) return -2;
716 sprintf(aaa, "SLRP %ld", msgnum);
719 sprintf(aaa, "SLRP HIGHEST");
721 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
727 int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
732 if (!cret) return -2;
733 if (!username) return -2;
735 aaa = (char *)malloc(strlen(username) + 6);
738 sprintf(aaa, "INVT %s", username);
739 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
746 int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
751 if (!cret) return -1;
752 if (!username) return -1;
754 aaa = (char *)malloc(strlen(username) + 6);
756 sprintf(aaa, "KICK %s", username);
757 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
764 int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
768 if (!cret) return -2;
769 if (!qret) return -2;
770 if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
771 if (!*qret) return -1;
773 ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
774 if (ret / 100 == 2) {
775 extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
776 extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
777 extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
778 qret[0]->QRflags = extract_int(cret, 3);
779 qret[0]->QRfloor = extract_int(cret, 4);
780 qret[0]->QRorder = extract_int(cret, 5);
781 qret[0]->QRdefaultview = extract_int(cret, 6);
782 qret[0]->QRflags2 = extract_int(cret, 7);
789 /* set forget to kick all users out of room */
790 int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
795 if (!cret) return -2;
796 if (!qret) return -2;
798 aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
799 strlen(qret->QRdirname) + 64);
802 sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
803 qret->QRname, qret->QRpasswd, qret->QRdirname,
804 qret->QRflags, forget, qret->QRfloor, qret->QRorder,
805 qret->QRdefaultview, qret->QRflags2);
806 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
813 int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
815 if (!cret) return -1;
817 return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
822 int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
827 if (!cret) return -2;
828 if (!username) return -2;
830 aaa = (char *)malloc(strlen(username) + 6);
833 sprintf(aaa, "SETA %s", username);
834 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
841 int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, const struct ctdlipcmessage *mr, char *cret)
846 if (!cret) return -2;
849 snprintf(cmd, sizeof cmd,
850 "ENT0 %d|%s|%d|%d|%s|%s", flag, mr->recipient,
851 mr->anonymous, mr->type, mr->subject, mr->author);
852 ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
859 int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
863 if (!cret) return -2;
864 if (!iret) return -2;
865 if (*iret) return -2;
867 return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
872 int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
876 if (!cret) return -2;
877 if (!msgnum) return -2;
879 sprintf(aaa, "DELE %ld", msgnum);
880 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
885 int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
890 if (!cret) return -2;
891 if (!destroom) return -2;
892 if (!msgnum) return -2;
894 aaa = (char *)malloc(strlen(destroom) + 28);
897 sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
898 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
905 int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
909 if (!cret) return -2;
911 sprintf(aaa, "KILL %d", for_real);
912 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
917 int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
918 const char *password, int floor, char *cret)
923 if (!cret) return -2;
924 if (!roomname) return -2;
927 aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
929 sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
932 aaa = (char *)malloc(strlen(roomname) + 40);
934 sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
937 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
944 int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
946 if (!cret) return -2;
948 return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
953 int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
959 if (!cret) return -2;
960 if (!mret) return -2;
961 if (*mret) return -2;
962 if (!message) return -2;
964 aaa = (char *)malloc(strlen(message) + 6);
967 sprintf(aaa, "MESG %s", message);
968 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
975 int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
977 if (!cret) return -2;
979 return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
984 int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
990 if (!cret) return -2;
991 if (!rret) return -2;
992 if (*rret) return -2;
995 aaa = (char *)malloc(strlen(username) + 6);
997 aaa = (char *)malloc(12);
1001 sprintf(aaa, "GREG %s", username);
1003 sprintf(aaa, "GREG _SELF_");
1004 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
1011 int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
1016 if (!cret) return -2;
1017 if (!username) return -2;
1018 if (axlevel < 0 || axlevel > 7) return -2;
1020 aaa = (char *)malloc(strlen(username) + 17);
1021 if (!aaa) return -1;
1023 sprintf(aaa, "VALI %s|%d", username, axlevel);
1024 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1031 int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
1035 if (!cret) return -1;
1036 if (!info) return -1;
1038 sprintf(aaa, "EINF %d", for_real);
1039 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1044 int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
1050 if (!cret) return -1;
1051 if (!listing) return -1;
1052 if (*listing) return -1;
1053 if (!searchstring) return -1;
1055 cmd = malloc(strlen(searchstring) + 10);
1056 sprintf(cmd, "LIST %s", searchstring);
1058 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
1065 int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
1067 if (!cret) return -1;
1068 if (!info) return -1;
1070 return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
1076 int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
1080 if (!cret) return -1;
1081 if (!chek) return -1;
1083 ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
1084 if (ret / 100 == 2) {
1085 chek->newmail = extract_long(cret, 0);
1086 chek->needregis = extract_int(cret, 1);
1087 chek->needvalid = extract_int(cret, 2);
1094 int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
1099 if (!cret) return -2;
1100 if (!filename) return -2;
1102 aaa = (char *)malloc(strlen(filename) + 6);
1103 if (!aaa) return -1;
1105 sprintf(aaa, "DELF %s", filename);
1106 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1113 int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
1118 if (!cret) return -2;
1119 if (!filename) return -2;
1120 if (!destroom) return -2;
1122 aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
1123 if (!aaa) return -1;
1125 sprintf(aaa, "MOVF %s|%s", filename, destroom);
1126 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1133 int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret)
1138 if (!cret) return -2;
1139 if (!filename) return -2;
1140 if (!destnode) return -2;
1142 aaa = (char *)malloc(strlen(filename) + strlen(destnode) + 7);
1143 if (!aaa) return -1;
1145 sprintf(aaa, "NETF %s|%s", filename, destnode);
1146 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1153 int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
1158 if (!cret) return -1;
1159 if (!listing) return -1;
1160 if (*listing) return -1;
1162 *stamp = CtdlIPCServerTime(ipc, cret);
1164 *stamp = time(NULL);
1165 ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
1171 int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
1173 void (*progress_gauge_callback)
1174 (CtdlIPC*, unsigned long, unsigned long),
1183 if (!cret) return -2;
1184 if (!filename) return -2;
1185 if (!buf) return -2;
1186 if (*buf) return -2;
1187 if (ipc->downloading) return -2;
1189 aaa = (char *)malloc(strlen(filename) + 6);
1190 if (!aaa) return -1;
1192 sprintf(aaa, "OPEN %s", filename);
1193 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1195 if (ret / 100 == 2) {
1196 ipc->downloading = 1;
1197 bytes = extract_long(cret, 0);
1198 last_mod = extract_int(cret, 1);
1199 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1201 ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
1202 progress_gauge_callback, cret);
1204 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
1205 progress_gauge_callback, cret);
1208 ret = CtdlIPCEndDownload(ipc, cret);
1210 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1211 filename, mimetype);
1218 int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
1220 void (*progress_gauge_callback)
1221 (CtdlIPC*, unsigned long, unsigned long),
1231 if (!cret) return -2;
1232 if (!buf) return -2;
1233 if (*buf) return -2;
1234 if (!part) return -2;
1235 if (!msgnum) return -2;
1236 if (ipc->downloading) return -2;
1238 sprintf(aaa, "OPNA %ld|%s", msgnum, part);
1239 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1240 if (ret / 100 == 2) {
1241 ipc->downloading = 1;
1242 bytes = extract_long(cret, 0);
1243 last_mod = extract_int(cret, 1);
1244 extract_token(filename, cret, 2, '|', sizeof filename);
1245 extract_token(mimetype, cret, 3, '|', sizeof mimetype);
1246 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1247 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1248 ret = CtdlIPCEndDownload(ipc, cret);
1250 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1251 filename, mimetype);
1258 int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
1259 void (*progress_gauge_callback)
1260 (CtdlIPC*, unsigned long, unsigned long),
1269 if (!cret) return -1;
1270 if (!buf) return -1;
1271 if (*buf) return -1;
1272 if (!filename) return -1;
1273 if (ipc->downloading) return -1;
1275 aaa = (char *)malloc(strlen(filename) + 6);
1276 if (!aaa) return -1;
1278 sprintf(aaa, "OIMG %s", filename);
1279 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1281 if (ret / 100 == 2) {
1282 ipc->downloading = 1;
1283 bytes = extract_long(cret, 0);
1284 last_mod = extract_int(cret, 1);
1285 extract_token(mimetype, cret, 2, '|', sizeof mimetype);
1286 /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
1287 ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
1288 ret = CtdlIPCEndDownload(ipc, cret);
1290 sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
1291 filename, mimetype);
1298 int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
1300 void (*progress_gauge_callback)
1301 (CtdlIPC*, unsigned long, unsigned long),
1307 if (!cret) return -1;
1308 if (!save_as) return -1;
1309 if (!comment) return -1;
1310 if (!path) return -1;
1311 if (!*path) return -1;
1312 if (ipc->uploading) return -1;
1314 aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
1315 if (!aaa) return -1;
1317 sprintf(aaa, "UOPN %s|%s", save_as, comment);
1318 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1320 if (ret / 100 == 2) {
1322 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1323 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1331 int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
1332 const char *save_as,
1333 void (*progress_gauge_callback)
1334 (CtdlIPC*, unsigned long, unsigned long),
1340 if (!cret) return -1;
1341 if (!save_as) return -1;
1342 if (!path && for_real) return -1;
1343 if (!*path && for_real) return -1;
1344 if (ipc->uploading) return -1;
1346 aaa = (char *)malloc(strlen(save_as) + 17);
1347 if (!aaa) return -1;
1349 sprintf(aaa, "UIMG %d|%s", for_real, save_as);
1350 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1352 if (ret / 100 == 2 && for_real) {
1354 ret = CtdlIPCWriteUpload(ipc, path, progress_gauge_callback, cret);
1355 ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
1363 int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
1368 if (!cret) return -2;
1369 if (!username) return -2;
1371 aaa = (char *)malloc(strlen(username) + 6);
1372 if (!aaa) return -1;
1374 sprintf(aaa, "QUSR %s", username);
1375 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1382 int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
1386 if (!cret) return -2;
1387 if (!listing) return -2;
1388 if (*listing) return -2;
1390 return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
1395 int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
1400 if (!cret) return -2;
1401 if (!name) return -2;
1403 sprintf(aaa, "CFLR %s|%d", name, for_real);
1404 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1410 int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
1414 if (!cret) return -1;
1415 if (floornum < 0) return -1;
1417 sprintf(aaa, "KFLR %d|%d", floornum, for_real);
1418 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1423 int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
1428 if (!cret) return -2;
1429 if (!floorname) return -2;
1430 if (floornum < 0) return -2;
1432 sprintf(aaa, "EFLR %d|%s", floornum, floorname);
1433 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1441 * You only need to fill out hostname, the defaults will be used if any of the
1442 * other fields are not set properly.
1444 int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
1445 int revision, const char *software_name, const char *hostname,
1451 if (developerid < 0 || clientid < 0 || revision < 0 ||
1455 revision = REV_LEVEL - 600;
1456 software_name = "Citadel (libcitadel)";
1458 if (!hostname) return -2;
1460 aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
1461 if (!aaa) return -1;
1463 sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
1464 revision, software_name, hostname);
1465 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1472 int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
1478 if (!cret) return -2;
1479 if (!username) return -2;
1481 aaa = (char *)malloc(strlen(username) + 8);
1482 if (!aaa) return -1;
1485 sprintf(aaa, "SEXP %s|-", username);
1486 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
1489 sprintf(aaa, "SEXP %s||", username);
1490 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1498 int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
1502 if (!cret) return -2;
1503 if (!listing) return -2;
1504 if (*listing) return -2;
1506 return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
1511 /* mode is 0 = enable, 1 = disable, 2 = status */
1512 int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
1516 if (!cret) return -2;
1518 sprintf(aaa, "DEXP %d", mode);
1519 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1524 int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
1526 if (!cret) return -2;
1527 if (!bio) return -2;
1529 return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
1535 int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
1541 if (!cret) return -2;
1542 if (!username) return -2;
1543 if (!listing) return -2;
1544 if (*listing) return -2;
1546 aaa = (char *)malloc(strlen(username) + 6);
1547 if (!aaa) return -1;
1549 sprintf(aaa, "RBIO %s", username);
1550 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
1557 int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
1561 if (!cret) return -2;
1562 if (!listing) return -2;
1563 if (*listing) return -2;
1565 return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
1570 int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
1574 if (!cret) return -1;
1576 sprintf(aaa, "STEL %d", mode ? 1 : 0);
1577 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1582 int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
1586 if (!cret) return -1;
1588 sprintf(aaa, "TERM %d", sid);
1589 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1594 int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
1596 if (!cret) return -1;
1598 return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
1603 int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
1607 if (!cret) return -1;
1609 sprintf(aaa, "SCDN %d", mode ? 1 : 0);
1610 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1615 int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
1621 if (!cret) return -2;
1622 if (!text) return -2;
1623 if (!filename) return -2;
1625 aaa = (char *)malloc(strlen(filename) + 6);
1626 if (!aaa) return -1;
1628 sprintf(aaa, "EMSG %s", filename);
1629 ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
1636 int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
1641 if (!cret) return -2;
1642 if (!hostname) return -2;
1644 aaa = (char *)malloc(strlen(hostname) + 6);
1645 if (!aaa) return -1;
1647 sprintf(aaa, "HCHG %s", hostname);
1648 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1655 int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
1660 if (!cret) return -2;
1661 if (!roomname) return -2;
1663 aaa = (char *)malloc(strlen(roomname) + 6);
1664 if (!aaa) return -1;
1666 sprintf(aaa, "RCHG %s", roomname);
1667 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1674 int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
1679 if (!cret) return -2;
1680 if (!username) return -2;
1682 aaa = (char *)malloc(strlen(username) + 6);
1683 if (!aaa) return -1;
1685 sprintf(aaa, "UCHG %s", username);
1686 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1693 /* This function returns the actual server time reported, or 0 if error */
1694 time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
1696 register time_t tret;
1699 ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
1700 if (ret / 100 == 2) {
1701 tret = extract_long(cret, 0);
1710 int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
1711 struct ctdluser **uret, char *cret)
1716 if (!cret) return -2;
1717 if (!uret) return -2;
1718 if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
1719 if (!*uret) return -1;
1721 sprintf(aaa, "AGUP %s", who);
1722 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1724 if (ret / 100 == 2) {
1725 extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
1726 extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
1727 uret[0]->flags = extract_int(cret, 2);
1728 uret[0]->timescalled = extract_long(cret, 3);
1729 uret[0]->posted = extract_long(cret, 4);
1730 uret[0]->axlevel = extract_int(cret, 5);
1731 uret[0]->usernum = extract_long(cret, 6);
1732 uret[0]->lastcall = extract_long(cret, 7);
1733 uret[0]->USuserpurge = extract_int(cret, 8);
1740 int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
1745 if (!cret) return -2;
1746 if (!uret) return -2;
1748 aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
1749 if (!aaa) return -1;
1751 sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
1752 uret->fullname, uret->password, uret->flags,
1753 uret->timescalled, uret->posted, uret->axlevel,
1754 uret->usernum, uret->lastcall, uret->USuserpurge);
1755 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1762 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1763 /* caller must free the struct ExpirePolicy */
1764 int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1765 struct ExpirePolicy **policy, char *cret)
1767 static char *proto[] = {"room", "floor", "site", "mailboxes" };
1771 if (!cret) return -2;
1772 if (!policy) return -2;
1773 if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
1774 if (!*policy) return -1;
1775 if (which < 0 || which > 3) return -2;
1777 sprintf(cmd, "GPEX %s", proto[which]);
1778 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
1779 if (ret / 100 == 2) {
1780 policy[0]->expire_mode = extract_int(cret, 0);
1781 policy[0]->expire_value = extract_int(cret, 1);
1788 /* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
1789 /* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
1790 int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
1791 struct ExpirePolicy *policy, char *cret)
1794 char *whichvals[] = { "room", "floor", "site", "mailboxes" };
1796 if (!cret) return -2;
1797 if (which < 0 || which > 3) return -2;
1798 if (!policy) return -2;
1799 if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
1800 if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
1802 sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
1803 policy->expire_mode, policy->expire_value);
1804 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1809 int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
1813 if (!cret) return -2;
1814 if (!listing) return -2;
1815 if (*listing) return -2;
1817 return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
1818 listing, &bytes, cret);
1823 int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
1825 if (!cret) return -2;
1826 if (!listing) return -2;
1828 return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
1834 int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1835 char **listing, char *cret)
1841 if (!cret) return -2;
1842 if (!mimetype) return -2;
1843 if (!listing) return -2;
1844 if (*listing) return -2;
1846 aaa = malloc(strlen(mimetype) + 13);
1847 if (!aaa) return -1;
1848 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1849 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1850 listing, &bytes, cret);
1857 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1858 const char *listing, char *cret)
1863 if (!cret) return -2;
1864 if (!mimetype) return -2;
1865 if (!listing) return -2;
1867 aaa = malloc(strlen(mimetype) + 13);
1868 if (!aaa) return -1;
1869 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1870 ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1878 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1882 if (!cret) return -2;
1883 if (!listing) return -2;
1884 if (*listing) return -2;
1886 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1887 listing, &bytes, cret);
1892 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1894 if (!cret) return -2;
1895 if (!listing) return -2;
1897 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1903 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1907 if (!cret) return -2;
1908 if (session < 0) return -2;
1910 sprintf(aaa, "REQT %d", session);
1911 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1916 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1920 if (!cret) return -2;
1921 if (msgnum < 0) return -2;
1923 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1924 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1929 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1938 /* New SSL object */
1939 temp_ssl = SSL_new(ssl_ctx);
1941 error_printf("SSL_new failed: %s\n",
1942 ERR_reason_error_string(ERR_get_error()));
1945 /* Pointless flag waving */
1946 #if SSLEAY_VERSION_NUMBER >= 0x0922
1947 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1950 if (!access(EGD_POOL, F_OK))
1953 if (!RAND_status()) {
1954 error_printf("PRNG not properly seeded\n");
1958 /* Associate network connection with SSL object */
1959 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1960 error_printf("SSL_set_fd failed: %s\n",
1961 ERR_reason_error_string(ERR_get_error()));
1965 if (status_hook != NULL)
1966 status_hook("Requesting encryption...\r");
1968 /* Ready to start SSL/TLS */
1970 CtdlIPC_putline(ipc, "STLS");
1971 CtdlIPC_getline(ipc, buf);
1972 if (buf[0] != '2') {
1973 error_printf("Server can't start TLS: %s\n", buf);
1977 r = CtdlIPCGenericCommand(ipc,
1978 "STLS", NULL, 0, NULL, NULL, cret);
1980 error_printf("Server can't start TLS: %s\n", buf);
1985 /* Do SSL/TLS handshake */
1986 if ((a = SSL_connect(temp_ssl)) < 1) {
1987 error_printf("SSL_connect failed: %s\n",
1988 ERR_reason_error_string(ERR_get_error()));
1992 ipc->ssl = temp_ssl;
1994 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1998 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1999 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
2000 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
2001 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2007 #endif /* HAVE_OPENSSL */
2012 static void endtls(SSL *ssl)
2023 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2028 if (!address) return -2;
2029 if (!cret) return -2;
2031 aaa = (char *)malloc(strlen(address) + 6);
2032 if (!aaa) return -1;
2034 sprintf(aaa, "QDIR %s", address);
2035 ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2042 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2046 if (!cret) return -2;
2047 sprintf(aaa, "IPGM %d", secret);
2048 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2053 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2057 if (!cret) return -2;
2058 if (!mret) return -2;
2059 if (*mret) return -2;
2061 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2082 /* ************************************************************************** */
2083 /* Stuff below this line is not for public consumption */
2084 /* ************************************************************************** */
2087 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2089 if (ipc->network_status_cb) ipc->network_status_cb(1);
2090 #ifdef THREADED_CLIENT
2091 pthread_mutex_lock(&(ipc->mutex));
2096 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2098 #ifdef THREADED_CLIENT
2099 pthread_mutex_unlock(&(ipc->mutex));
2101 if (ipc->network_status_cb) ipc->network_status_cb(0);
2105 /* Read a listing from the server up to 000. Append to dest if it exists */
2106 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2115 length = strlen(ret);
2120 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2121 linelength = strlen(aaa);
2122 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2124 strcpy(&ret[length], aaa);
2125 length += linelength;
2126 strcpy(&ret[length++], "\n");
2134 /* Send a listing to the server; generate the ending 000. */
2135 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2139 text = (char *)malloc(strlen(listing) + 6);
2141 strcpy(text, listing);
2142 while (text[strlen(text) - 1] == '\n')
2143 text[strlen(text) - 1] = '\0';
2144 strcat(text, "\n000");
2145 CtdlIPC_putline(ipc, text);
2149 /* Malloc failed but we are committed to send */
2150 /* This may result in extra blanks at the bottom */
2151 CtdlIPC_putline(ipc, text);
2152 CtdlIPC_putline(ipc, "000");
2158 /* Partial read of file from server */
2159 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2161 register size_t len = 0;
2165 if (!cret) return 0;
2166 if (bytes < 1) return 0;
2169 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2170 CtdlIPC_putline(ipc, aaa);
2171 CtdlIPC_getline(ipc, aaa);
2173 strcpy(cret, &aaa[4]);
2175 len = extract_long(&aaa[4], 0);
2176 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2178 /* I know what I'm doing */
2179 serv_read(ipc, ((char *)(*buf) + offset), len);
2181 /* We have to read regardless */
2182 serv_read(ipc, aaa, len);
2186 CtdlIPC_unlock(ipc);
2192 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2196 if (!cret) return -2;
2197 if (!ipc->downloading) return -2;
2199 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2201 ipc->downloading = 0;
2207 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2211 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2212 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2219 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2220 void (*progress_gauge_callback)
2221 (CtdlIPC*, unsigned long, unsigned long),
2224 register size_t len;
2226 if (!cret) return -1;
2227 if (!buf) return -1;
2228 if (*buf) return -1;
2229 if (!ipc->downloading) return -1;
2232 if (progress_gauge_callback)
2233 progress_gauge_callback(ipc, len, bytes);
2234 while (len < bytes) {
2235 register size_t block;
2237 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2243 if (progress_gauge_callback)
2244 progress_gauge_callback(ipc, len, bytes);
2249 /* READ - pipelined */
2250 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2252 void (*progress_gauge_callback)
2253 (CtdlIPC*, unsigned long, unsigned long),
2256 register size_t len;
2257 register int calls; /* How many calls in the pipeline */
2258 register int i; /* iterator */
2261 if (!cret) return -1;
2262 if (!buf) return -1;
2263 if (*buf) return -1;
2264 if (!ipc->downloading) return -1;
2266 *buf = (void *)realloc(*buf, bytes - resume);
2267 if (!*buf) return -1;
2271 if (progress_gauge_callback)
2272 progress_gauge_callback(ipc, len, bytes);
2274 /* How many calls will be in the pipeline? */
2275 calls = (bytes - resume) / 4096;
2276 if ((bytes - resume) % 4096) calls++;
2278 /* Send all requests at once */
2279 for (i = 0; i < calls; i++) {
2280 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2281 CtdlIPC_putline(ipc, aaa);
2284 /* Receive all responses at once */
2285 for (i = 0; i < calls; i++) {
2286 CtdlIPC_getline(ipc, aaa);
2288 strcpy(cret, &aaa[4]);
2290 len = extract_long(&aaa[4], 0);
2291 /* I know what I'm doing */
2292 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2294 if (progress_gauge_callback)
2295 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2297 CtdlIPC_unlock(ipc);
2303 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2308 if (!cret) return -1;
2309 if (!ipc->uploading) return -1;
2311 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2312 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2319 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2320 void (*progress_gauge_callback)
2321 (CtdlIPC*, unsigned long, unsigned long),
2324 register int ret = -1;
2325 register size_t offset = 0;
2331 if (!cret) return -1;
2332 if (!path) return -1;
2333 if (!*path) return -1;
2335 fd = fopen(path, "r");
2338 fseek(fd, 0L, SEEK_END);
2342 if (progress_gauge_callback)
2343 progress_gauge_callback(ipc, 0, bytes);
2345 while (offset < bytes) {
2346 register size_t to_write;
2348 /* Read some data in */
2349 to_write = fread(buf, 1, 4096, fd);
2351 if (feof(fd) || ferror(fd)) break;
2353 sprintf(aaa, "WRIT %d", (int)to_write);
2354 CtdlIPC_putline(ipc, aaa);
2355 CtdlIPC_getline(ipc, aaa);
2356 strcpy(cret, &aaa[4]);
2358 if (aaa[0] == '7') {
2359 to_write = extract_long(&aaa[4], 0);
2361 serv_write(ipc, buf, to_write);
2363 if (progress_gauge_callback)
2364 progress_gauge_callback(ipc, offset, bytes);
2365 /* Detect short reads and back up if needed */
2366 /* offset will never be negative anyway */
2367 fseek(fd, (signed)offset, SEEK_SET);
2372 if (progress_gauge_callback)
2373 progress_gauge_callback(ipc, 1, 1);
2374 return (!ferror(fd) ? ret : -2);
2379 * Generic command method. This method should handle any server command
2380 * except for CHAT. It takes the following arguments:
2382 * ipc The server to speak with
2383 * command Preformatted command to send to server
2384 * to_send A text or binary file to send to server
2385 * (only sent if server requests it)
2386 * bytes_to_send The number of bytes in to_send (required if
2387 * sending binary, optional if sending listing)
2388 * to_receive Pointer to a NULL pointer, if the server
2389 * sends text or binary we will allocate memory
2390 * for the file and stuff it here
2391 * bytes_to_receive If a file is received, we will store its
2393 * proto_response The protocol response. Caller must provide
2394 * this buffer and ensure that it is at least
2395 * 128 bytes in length.
2397 * This function returns a number equal to the protocol response number,
2398 * -1 if an internal error occurred, -2 if caller provided bad values,
2399 * or 0 - the protocol response number if bad values were found during
2400 * the protocol exchange.
2401 * It stores the protocol response string (minus the number) in
2402 * protocol_response as described above. Some commands send additional
2403 * data in this string.
2405 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2406 const char *command, const char *to_send,
2407 size_t bytes_to_send, char **to_receive,
2408 size_t *bytes_to_receive, char *proto_response)
2414 if (!command) return -2;
2415 if (!proto_response) return -2;
2418 if (ipc->ssl) watch_ssl = 1;
2422 CtdlIPC_putline(ipc, command);
2424 CtdlIPC_getline(ipc, proto_response);
2425 if (proto_response[3] == '*')
2427 ret = atoi(proto_response);
2428 strcpy(proto_response, &proto_response[4]);
2429 switch (ret / 100) {
2430 default: /* Unknown, punt */
2432 case 3: /* MORE_DATA */
2434 /* Don't need to do anything */
2436 case 1: /* LISTING_FOLLOWS */
2437 if (to_receive && !*to_receive && bytes_to_receive) {
2438 *to_receive = CtdlIPCReadListing(ipc, NULL);
2439 } else { /* Drain */
2440 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2444 case 4: /* SEND_LISTING */
2446 CtdlIPCSendListing(ipc, to_send);
2448 /* No listing given, fake it */
2449 CtdlIPC_putline(ipc, "000");
2453 case 6: /* BINARY_FOLLOWS */
2454 if (to_receive && !*to_receive && bytes_to_receive) {
2456 extract_long(proto_response, 0);
2457 *to_receive = (char *)
2458 malloc((size_t)*bytes_to_receive);
2462 serv_read(ipc, *to_receive,
2469 drain = extract_long(proto_response, 0);
2470 while (drain > SIZ) {
2471 serv_read(ipc, buf, SIZ);
2474 serv_read(ipc, buf, drain);
2478 case 7: /* SEND_BINARY */
2479 if (to_send && bytes_to_send) {
2480 serv_write(ipc, to_send, bytes_to_send);
2481 } else if (bytes_to_send) {
2482 /* Fake it, send nulls */
2485 fake = bytes_to_send;
2486 memset(buf, '\0', SIZ);
2487 while (fake > SIZ) {
2488 serv_write(ipc, buf, SIZ);
2491 serv_write(ipc, buf, fake);
2493 } /* else who knows? DANGER WILL ROBINSON */
2495 case 8: /* START_CHAT_MODE */
2496 if (!strncasecmp(command, "CHAT", 4)) {
2497 /* Don't call chatmode with generic! */
2498 CtdlIPC_putline(ipc, "/quit");
2501 /* In this mode we send then receive listing */
2503 CtdlIPCSendListing(ipc, to_send);
2505 /* No listing given, fake it */
2506 CtdlIPC_putline(ipc, "000");
2509 if (to_receive && !*to_receive
2510 && bytes_to_receive) {
2511 *to_receive = CtdlIPCReadListing(ipc, NULL);
2512 } else { /* Drain */
2513 while (CtdlIPC_getline(ipc, buf),
2514 strcmp(buf, "000")) ;
2519 case 9: /* ASYNC_MSG */
2520 /* CtdlIPCDoAsync(ret, proto_response); */
2521 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2527 CtdlIPC_unlock(ipc);
2532 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2534 struct hostent *phe;
2535 struct servent *pse;
2536 struct protoent *ppe;
2537 struct sockaddr_in sin;
2540 memset(&sin, 0, sizeof(sin));
2541 sin.sin_family = AF_INET;
2543 pse = getservbyname(service, protocol);
2545 sin.sin_port = pse->s_port;
2547 else if (atoi(service) > 0) {
2548 sin.sin_port = htons(atoi(service));
2551 sin.sin_port = htons(defaultPort);
2553 phe = gethostbyname(host);
2555 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2556 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2559 if ((ppe = getprotobyname(protocol)) == 0) {
2562 if (!strcmp(protocol, "udp")) {
2568 s = socket(PF_INET, type, ppe->p_proto);
2573 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2581 static int uds_connectsock(int *isLocal, char *sockpath)
2583 struct sockaddr_un addr;
2586 memset(&addr, 0, sizeof(addr));
2587 addr.sun_family = AF_UNIX;
2588 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2590 s = socket(AF_UNIX, SOCK_STREAM, 0);
2595 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2606 * input binary data from socket
2608 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2610 unsigned int len, rlen;
2612 #if defined(HAVE_OPENSSL)
2614 serv_read_ssl(ipc, buf, bytes);
2619 while (len < bytes) {
2620 rlen = read(ipc->sock, &buf[len], bytes - len);
2622 connection_died(ipc, 0);
2631 * send binary to server
2633 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2635 unsigned int bytes_written = 0;
2638 #if defined(HAVE_OPENSSL)
2640 serv_write_ssl(ipc, buf, nbytes);
2644 while (bytes_written < nbytes) {
2645 retval = write(ipc->sock, &buf[bytes_written],
2646 nbytes - bytes_written);
2648 connection_died(ipc, 0);
2651 bytes_written += retval;
2658 * input binary data from encrypted connection
2660 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2666 while (len < bytes) {
2667 if (SSL_want_read(ipc->ssl)) {
2668 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2669 error_printf("SSL_write in serv_read:\n");
2670 ERR_print_errors_fp(stderr);
2673 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2677 errval = SSL_get_error(ipc->ssl, rlen);
2678 if (errval == SSL_ERROR_WANT_READ ||
2679 errval == SSL_ERROR_WANT_WRITE) {
2684 Not sure why we'd want to handle these error codes any differently,
2685 but this definitely isn't the way to handle them. Someone must have
2686 naively assumed that we could fall back to unencrypted communications,
2687 but all it does is just recursively blow the stack.
2688 if (errval == SSL_ERROR_ZERO_RETURN ||
2689 errval == SSL_ERROR_SSL) {
2690 serv_read(ipc, &buf[len], bytes - len);
2694 error_printf("SSL_read in serv_read: %s\n",
2695 ERR_reason_error_string(ERR_peek_error()));
2696 connection_died(ipc, 1);
2705 * send binary to server encrypted
2707 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2709 unsigned int bytes_written = 0;
2713 while (bytes_written < nbytes) {
2714 if (SSL_want_write(ipc->ssl)) {
2715 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2716 error_printf("SSL_read in serv_write:\n");
2717 ERR_print_errors_fp(stderr);
2720 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2721 nbytes - bytes_written);
2725 errval = SSL_get_error(ipc->ssl, retval);
2726 if (errval == SSL_ERROR_WANT_READ ||
2727 errval == SSL_ERROR_WANT_WRITE) {
2731 if (errval == SSL_ERROR_ZERO_RETURN ||
2732 errval == SSL_ERROR_SSL) {
2733 serv_write(ipc, &buf[bytes_written],
2734 nbytes - bytes_written);
2737 error_printf("SSL_write in serv_write: %s\n",
2738 ERR_reason_error_string(ERR_peek_error()));
2739 connection_died(ipc, 1);
2742 bytes_written += retval;
2747 static void CtdlIPC_init_OpenSSL(void)
2750 SSL_METHOD *ssl_method;
2753 /* already done init */
2762 SSL_load_error_strings();
2763 SSLeay_add_ssl_algorithms();
2765 /* Set up the SSL context in which we will oeprate */
2766 ssl_method = SSLv23_client_method();
2767 ssl_ctx = SSL_CTX_new(ssl_method);
2769 error_printf("SSL_CTX_new failed: %s\n",
2770 ERR_reason_error_string(ERR_get_error()));
2773 /* Any reasonable cipher we can get */
2774 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2775 error_printf("No ciphers available for encryption\n");
2778 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2780 /* Load DH parameters into the context */
2783 error_printf("Can't allocate a DH object: %s\n",
2784 ERR_reason_error_string(ERR_get_error()));
2787 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2788 error_printf("Can't assign DH_P: %s\n",
2789 ERR_reason_error_string(ERR_get_error()));
2793 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2794 error_printf("Can't assign DH_G: %s\n",
2795 ERR_reason_error_string(ERR_get_error()));
2800 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2803 #ifdef THREADED_CLIENT
2804 /* OpenSSL requires callbacks for threaded clients */
2805 CRYPTO_set_locking_callback(ssl_lock);
2806 CRYPTO_set_id_callback(id_callback);
2808 /* OpenSSL requires us to do semaphores for threaded clients */
2809 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2811 perror("malloc failed");
2814 for (a = 0; a < CRYPTO_num_locks(); a++) {
2815 Critters[a] = malloc(sizeof (pthread_mutex_t));
2817 perror("malloc failed");
2820 pthread_mutex_init(Critters[a], NULL);
2823 #endif /* THREADED_CLIENT */
2827 static void ssl_lock(int mode, int n, const char *file, int line)
2829 #ifdef THREADED_CLIENT
2830 if (mode & CRYPTO_LOCK)
2831 pthread_mutex_lock(Critters[n]);
2833 pthread_mutex_unlock(Critters[n]);
2834 #endif /* THREADED_CLIENT */
2837 #ifdef THREADED_CLIENT
2838 static unsigned long id_callback(void) {
2839 return (unsigned long)pthread_self();
2841 #endif /* THREADED_CLIENT */
2842 #endif /* HAVE_OPENSSL */
2846 * input string from socket - implemented in terms of serv_read()
2848 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2852 /* Read one character at a time. */
2854 serv_read(ipc, &buf[i], 1);
2855 if (buf[i] == '\n' || i == (SIZ-1))
2859 /* If we got a long line, discard characters until the newline. */
2861 while (buf[i] != '\n')
2862 serv_read(ipc, &buf[i], 1);
2864 /* Strip the trailing newline (and carriage return, if present) */
2865 if (buf[i] == 10) buf[i--] = 0;
2866 if (buf[i] == 13) buf[i--] = 0;
2869 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2871 CtdlIPC_getline(ipc, buf);
2875 * send line to server - implemented in terms of serv_write()
2877 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2883 cmd = malloc(len + 2);
2885 /* This requires no extra memory */
2886 serv_write(ipc, buf, len);
2887 serv_write(ipc, "\n", 1);
2889 /* This is network-optimized */
2890 strncpy(cmd, buf, len);
2891 strcpy(cmd + len, "\n");
2892 serv_write(ipc, cmd, len + 1);
2896 ipc->last_command_sent = time(NULL);
2899 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
2901 CtdlIPC_putline(ipc, buf);
2908 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2916 ipc = ialloc(CtdlIPC);
2920 #if defined(HAVE_OPENSSL)
2922 CtdlIPC_init_OpenSSL();
2924 #if defined(HAVE_PTHREAD_H)
2925 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2927 ipc->sock = -1; /* Not connected */
2928 ipc->isLocal = 0; /* Not local, of course! */
2929 ipc->downloading = 0;
2931 ipc->last_command_sent = 0L;
2932 ipc->network_status_cb = NULL;
2934 strcpy(cithost, DEFAULT_HOST); /* default host */
2935 strcpy(citport, DEFAULT_PORT); /* default port */
2937 /* Allow caller to supply our values (Windows) */
2938 if (hostbuf && strlen(hostbuf) > 0)
2939 strcpy(cithost, hostbuf);
2940 if (portbuf && strlen(portbuf) > 0)
2941 strcpy(citport, portbuf);
2943 /* Read host/port from command line if present */
2944 for (a = 0; a < argc; ++a) {
2947 } else if (a == 1) {
2948 strcpy(cithost, argv[a]);
2949 } else if (a == 2) {
2950 strcpy(citport, argv[a]);
2952 error_printf("%s: usage: ",argv[0]);
2953 error_printf("%s [host] [port] ",argv[0]);
2960 if ((!strcmp(cithost, "localhost"))
2961 || (!strcmp(cithost, "127.0.0.1"))) {
2965 /* If we're using a unix domain socket we can do a bunch of stuff */
2966 if (!strcmp(cithost, UDS)) {
2967 if (!strcasecmp(citport, DEFAULT_PORT)) {
2968 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
2971 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
2973 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2974 if (ipc->sock == -1) {
2978 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2979 if (portbuf != NULL) strcpy(portbuf, sockpath);
2983 ipc->sock = connectsock(cithost, citport, "tcp", 504);
2984 if (ipc->sock == -1) {
2988 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2989 if (portbuf != NULL) strcpy(portbuf, citport);
2995 * Disconnect and delete the IPC class (destructor)
2997 void CtdlIPC_delete(CtdlIPC* ipc)
3001 SSL_shutdown(ipc->ssl);
3006 if (ipc->sock > -1) {
3007 shutdown(ipc->sock, 2); /* Close it up */
3015 * Disconnect and delete the IPC class (destructor)
3016 * Also NULLs out the pointer
3018 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3020 CtdlIPC_delete(*pipc);
3026 * return the file descriptor of the server socket so we can select() on it.
3028 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3031 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3038 * return one character
3040 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3043 char CtdlIPC_get(CtdlIPC* ipc)
3048 serv_read(ipc, buf, 1);