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)
1840 if (!cret) return -2;
1841 if (!mimetype) return -2;
1842 if (!listing) return -2;
1843 if (*listing) return -2;
1845 aaa = malloc(strlen(mimetype) + 13);
1846 if (!aaa) return -1;
1847 sprintf(aaa, "CONF GETSYS|%s", mimetype);
1848 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
1849 listing, &bytes, cret);
1854 int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
1855 const char *listing, char *cret)
1859 if (!cret) return -2;
1860 if (!mimetype) return -2;
1861 if (!listing) return -2;
1863 aaa = malloc(strlen(mimetype) + 13);
1864 if (!aaa) return -1;
1865 sprintf(aaa, "CONF PUTSYS|%s", mimetype);
1866 return CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
1872 int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
1876 if (!cret) return -2;
1877 if (!listing) return -2;
1878 if (*listing) return -2;
1880 return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
1881 listing, &bytes, cret);
1886 int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
1888 if (!cret) return -2;
1889 if (!listing) return -2;
1891 return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
1897 int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
1901 if (!cret) return -2;
1902 if (session < 0) return -2;
1904 sprintf(aaa, "REQT %d", session);
1905 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1910 int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
1914 if (!cret) return -2;
1915 if (msgnum < 0) return -2;
1917 sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
1918 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
1923 int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
1932 /* New SSL object */
1933 temp_ssl = SSL_new(ssl_ctx);
1935 error_printf("SSL_new failed: %s\n",
1936 ERR_reason_error_string(ERR_get_error()));
1939 /* Pointless flag waving */
1940 #if SSLEAY_VERSION_NUMBER >= 0x0922
1941 SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
1944 if (!access(EGD_POOL, F_OK))
1947 if (!RAND_status()) {
1948 error_printf("PRNG not properly seeded\n");
1952 /* Associate network connection with SSL object */
1953 if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
1954 error_printf("SSL_set_fd failed: %s\n",
1955 ERR_reason_error_string(ERR_get_error()));
1959 if (status_hook != NULL)
1960 status_hook("Requesting encryption...\r");
1962 /* Ready to start SSL/TLS */
1964 CtdlIPC_putline(ipc, "STLS");
1965 CtdlIPC_getline(ipc, buf);
1966 if (buf[0] != '2') {
1967 error_printf("Server can't start TLS: %s\n", buf);
1971 r = CtdlIPCGenericCommand(ipc,
1972 "STLS", NULL, 0, NULL, NULL, cret);
1974 error_printf("Server can't start TLS: %s\n", buf);
1979 /* Do SSL/TLS handshake */
1980 if ((a = SSL_connect(temp_ssl)) < 1) {
1981 error_printf("SSL_connect failed: %s\n",
1982 ERR_reason_error_string(ERR_get_error()));
1986 ipc->ssl = temp_ssl;
1988 BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
1992 bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
1993 error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
1994 SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
1995 SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
2001 #endif /* HAVE_OPENSSL */
2006 static void endtls(SSL *ssl)
2017 int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
2021 if (!address) return -2;
2022 if (!cret) return -2;
2024 aaa = (char *)malloc(strlen(address) + 6);
2025 if (!aaa) return -1;
2027 sprintf(aaa, "QDIR %s", address);
2028 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2033 int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
2037 if (!cret) return -2;
2038 sprintf(aaa, "IPGM %d", secret);
2039 return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
2044 int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
2048 if (!cret) return -2;
2049 if (!mret) return -2;
2050 if (*mret) return -2;
2052 return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
2073 /* ************************************************************************** */
2074 /* Stuff below this line is not for public consumption */
2075 /* ************************************************************************** */
2078 INLINE void CtdlIPC_lock(CtdlIPC *ipc)
2080 if (ipc->network_status_cb) ipc->network_status_cb(1);
2081 #ifdef THREADED_CLIENT
2082 pthread_mutex_lock(&(ipc->mutex));
2087 INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
2089 #ifdef THREADED_CLIENT
2090 pthread_mutex_unlock(&(ipc->mutex));
2092 if (ipc->network_status_cb) ipc->network_status_cb(0);
2096 /* Read a listing from the server up to 000. Append to dest if it exists */
2097 char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
2106 length = strlen(ret);
2111 while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
2112 linelength = strlen(aaa);
2113 ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
2115 strcpy(&ret[length], aaa);
2116 length += linelength;
2117 strcpy(&ret[length++], "\n");
2125 /* Send a listing to the server; generate the ending 000. */
2126 int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
2130 text = (char *)malloc(strlen(listing) + 6);
2132 strcpy(text, listing);
2133 while (text[strlen(text) - 1] == '\n')
2134 text[strlen(text) - 1] = '\0';
2135 strcat(text, "\n000");
2136 CtdlIPC_putline(ipc, text);
2140 /* Malloc failed but we are committed to send */
2141 /* This may result in extra blanks at the bottom */
2142 CtdlIPC_putline(ipc, text);
2143 CtdlIPC_putline(ipc, "000");
2149 /* Partial read of file from server */
2150 size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
2152 register size_t len = 0;
2156 if (!cret) return 0;
2157 if (bytes < 1) return 0;
2160 sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
2161 CtdlIPC_putline(ipc, aaa);
2162 CtdlIPC_getline(ipc, aaa);
2164 strcpy(cret, &aaa[4]);
2166 len = extract_long(&aaa[4], 0);
2167 *buf = (void *)realloc(*buf, (size_t)(offset + len));
2169 /* I know what I'm doing */
2170 serv_read(ipc, ((char *)(*buf) + offset), len);
2172 /* We have to read regardless */
2173 serv_read(ipc, aaa, len);
2177 CtdlIPC_unlock(ipc);
2183 int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
2187 if (!cret) return -2;
2188 if (!ipc->downloading) return -2;
2190 ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
2192 ipc->downloading = 0;
2198 int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
2202 snprintf(cmd, sizeof cmd, "MSGP %s", formats);
2203 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2210 int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
2211 void (*progress_gauge_callback)
2212 (CtdlIPC*, unsigned long, unsigned long),
2215 register size_t len;
2217 if (!cret) return -1;
2218 if (!buf) return -1;
2219 if (*buf) return -1;
2220 if (!ipc->downloading) return -1;
2223 if (progress_gauge_callback)
2224 progress_gauge_callback(ipc, len, bytes);
2225 while (len < bytes) {
2226 register size_t block;
2228 block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
2234 if (progress_gauge_callback)
2235 progress_gauge_callback(ipc, len, bytes);
2240 /* READ - pipelined */
2241 int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
2243 void (*progress_gauge_callback)
2244 (CtdlIPC*, unsigned long, unsigned long),
2247 register size_t len;
2248 register int calls; /* How many calls in the pipeline */
2249 register int i; /* iterator */
2252 if (!cret) return -1;
2253 if (!buf) return -1;
2254 if (*buf) return -1;
2255 if (!ipc->downloading) return -1;
2257 *buf = (void *)realloc(*buf, bytes - resume);
2258 if (!*buf) return -1;
2262 if (progress_gauge_callback)
2263 progress_gauge_callback(ipc, len, bytes);
2265 /* How many calls will be in the pipeline? */
2266 calls = (bytes - resume) / 4096;
2267 if ((bytes - resume) % 4096) calls++;
2269 /* Send all requests at once */
2270 for (i = 0; i < calls; i++) {
2271 sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
2272 CtdlIPC_putline(ipc, aaa);
2275 /* Receive all responses at once */
2276 for (i = 0; i < calls; i++) {
2277 CtdlIPC_getline(ipc, aaa);
2279 strcpy(cret, &aaa[4]);
2281 len = extract_long(&aaa[4], 0);
2282 /* I know what I'm doing */
2283 serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
2285 if (progress_gauge_callback)
2286 progress_gauge_callback(ipc, i * 4096 + len, bytes);
2288 CtdlIPC_unlock(ipc);
2294 int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
2299 if (!cret) return -1;
2300 if (!ipc->uploading) return -1;
2302 sprintf(cmd, "UCLS %d", discard ? 0 : 1);
2303 ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
2310 int CtdlIPCWriteUpload(CtdlIPC *ipc, const char *path,
2311 void (*progress_gauge_callback)
2312 (CtdlIPC*, unsigned long, unsigned long),
2315 register int ret = -1;
2316 register size_t offset = 0;
2322 if (!cret) return -1;
2323 if (!path) return -1;
2324 if (!*path) return -1;
2326 fd = fopen(path, "r");
2329 fseek(fd, 0L, SEEK_END);
2333 if (progress_gauge_callback)
2334 progress_gauge_callback(ipc, 0, bytes);
2336 while (offset < bytes) {
2337 register size_t to_write;
2339 /* Read some data in */
2340 to_write = fread(buf, 1, 4096, fd);
2342 if (feof(fd) || ferror(fd)) break;
2344 sprintf(aaa, "WRIT %d", (int)to_write);
2345 CtdlIPC_putline(ipc, aaa);
2346 CtdlIPC_getline(ipc, aaa);
2347 strcpy(cret, &aaa[4]);
2349 if (aaa[0] == '7') {
2350 to_write = extract_long(&aaa[4], 0);
2352 serv_write(ipc, buf, to_write);
2354 if (progress_gauge_callback)
2355 progress_gauge_callback(ipc, offset, bytes);
2356 /* Detect short reads and back up if needed */
2357 /* offset will never be negative anyway */
2358 fseek(fd, (signed)offset, SEEK_SET);
2363 if (progress_gauge_callback)
2364 progress_gauge_callback(ipc, 1, 1);
2365 return (!ferror(fd) ? ret : -2);
2370 * Generic command method. This method should handle any server command
2371 * except for CHAT. It takes the following arguments:
2373 * ipc The server to speak with
2374 * command Preformatted command to send to server
2375 * to_send A text or binary file to send to server
2376 * (only sent if server requests it)
2377 * bytes_to_send The number of bytes in to_send (required if
2378 * sending binary, optional if sending listing)
2379 * to_receive Pointer to a NULL pointer, if the server
2380 * sends text or binary we will allocate memory
2381 * for the file and stuff it here
2382 * bytes_to_receive If a file is received, we will store its
2384 * proto_response The protocol response. Caller must provide
2385 * this buffer and ensure that it is at least
2386 * 128 bytes in length.
2388 * This function returns a number equal to the protocol response number,
2389 * -1 if an internal error occurred, -2 if caller provided bad values,
2390 * or 0 - the protocol response number if bad values were found during
2391 * the protocol exchange.
2392 * It stores the protocol response string (minus the number) in
2393 * protocol_response as described above. Some commands send additional
2394 * data in this string.
2396 int CtdlIPCGenericCommand(CtdlIPC *ipc,
2397 const char *command, const char *to_send,
2398 size_t bytes_to_send, char **to_receive,
2399 size_t *bytes_to_receive, char *proto_response)
2405 if (!command) return -2;
2406 if (!proto_response) return -2;
2409 if (ipc->ssl) watch_ssl = 1;
2413 CtdlIPC_putline(ipc, command);
2415 CtdlIPC_getline(ipc, proto_response);
2416 if (proto_response[3] == '*')
2418 ret = atoi(proto_response);
2419 strcpy(proto_response, &proto_response[4]);
2420 switch (ret / 100) {
2421 default: /* Unknown, punt */
2423 case 3: /* MORE_DATA */
2425 /* Don't need to do anything */
2427 case 1: /* LISTING_FOLLOWS */
2428 if (to_receive && !*to_receive && bytes_to_receive) {
2429 *to_receive = CtdlIPCReadListing(ipc, NULL);
2430 } else { /* Drain */
2431 while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
2435 case 4: /* SEND_LISTING */
2437 CtdlIPCSendListing(ipc, to_send);
2439 /* No listing given, fake it */
2440 CtdlIPC_putline(ipc, "000");
2444 case 6: /* BINARY_FOLLOWS */
2445 if (to_receive && !*to_receive && bytes_to_receive) {
2447 extract_long(proto_response, 0);
2448 *to_receive = (char *)
2449 malloc((size_t)*bytes_to_receive);
2453 serv_read(ipc, *to_receive,
2460 drain = extract_long(proto_response, 0);
2461 while (drain > SIZ) {
2462 serv_read(ipc, buf, SIZ);
2465 serv_read(ipc, buf, drain);
2469 case 7: /* SEND_BINARY */
2470 if (to_send && bytes_to_send) {
2471 serv_write(ipc, to_send, bytes_to_send);
2472 } else if (bytes_to_send) {
2473 /* Fake it, send nulls */
2476 fake = bytes_to_send;
2477 memset(buf, '\0', SIZ);
2478 while (fake > SIZ) {
2479 serv_write(ipc, buf, SIZ);
2482 serv_write(ipc, buf, fake);
2484 } /* else who knows? DANGER WILL ROBINSON */
2486 case 8: /* START_CHAT_MODE */
2487 if (!strncasecmp(command, "CHAT", 4)) {
2488 /* Don't call chatmode with generic! */
2489 CtdlIPC_putline(ipc, "/quit");
2492 /* In this mode we send then receive listing */
2494 CtdlIPCSendListing(ipc, to_send);
2496 /* No listing given, fake it */
2497 CtdlIPC_putline(ipc, "000");
2500 if (to_receive && !*to_receive
2501 && bytes_to_receive) {
2502 *to_receive = CtdlIPCReadListing(ipc, NULL);
2503 } else { /* Drain */
2504 while (CtdlIPC_getline(ipc, buf),
2505 strcmp(buf, "000")) ;
2510 case 9: /* ASYNC_MSG */
2511 /* CtdlIPCDoAsync(ret, proto_response); */
2512 free(CtdlIPCReadListing(ipc, NULL)); /* STUB FIXME */
2518 CtdlIPC_unlock(ipc);
2523 static int connectsock(char *host, char *service, char *protocol, int defaultPort)
2525 struct hostent *phe;
2526 struct servent *pse;
2527 struct protoent *ppe;
2528 struct sockaddr_in sin;
2531 memset(&sin, 0, sizeof(sin));
2532 sin.sin_family = AF_INET;
2534 pse = getservbyname(service, protocol);
2536 sin.sin_port = pse->s_port;
2538 else if (atoi(service) > 0) {
2539 sin.sin_port = htons(atoi(service));
2542 sin.sin_port = htons(defaultPort);
2544 phe = gethostbyname(host);
2546 memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
2547 } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
2550 if ((ppe = getprotobyname(protocol)) == 0) {
2553 if (!strcmp(protocol, "udp")) {
2559 s = socket(PF_INET, type, ppe->p_proto);
2564 if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
2572 static int uds_connectsock(int *isLocal, char *sockpath)
2574 struct sockaddr_un addr;
2577 memset(&addr, 0, sizeof(addr));
2578 addr.sun_family = AF_UNIX;
2579 safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
2581 s = socket(AF_UNIX, SOCK_STREAM, 0);
2586 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
2597 * input binary data from socket
2599 static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
2601 unsigned int len, rlen;
2603 #if defined(HAVE_OPENSSL)
2605 serv_read_ssl(ipc, buf, bytes);
2610 while (len < bytes) {
2611 rlen = read(ipc->sock, &buf[len], bytes - len);
2613 connection_died(ipc, 0);
2622 * send binary to server
2624 static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2626 unsigned int bytes_written = 0;
2629 #if defined(HAVE_OPENSSL)
2631 serv_write_ssl(ipc, buf, nbytes);
2635 while (bytes_written < nbytes) {
2636 retval = write(ipc->sock, &buf[bytes_written],
2637 nbytes - bytes_written);
2639 connection_died(ipc, 0);
2642 bytes_written += retval;
2649 * input binary data from encrypted connection
2651 static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
2657 while (len < bytes) {
2658 if (SSL_want_read(ipc->ssl)) {
2659 if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
2660 error_printf("SSL_write in serv_read:\n");
2661 ERR_print_errors_fp(stderr);
2664 rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
2668 errval = SSL_get_error(ipc->ssl, rlen);
2669 if (errval == SSL_ERROR_WANT_READ ||
2670 errval == SSL_ERROR_WANT_WRITE) {
2675 Not sure why we'd want to handle these error codes any differently,
2676 but this definitely isn't the way to handle them. Someone must have
2677 naively assumed that we could fall back to unencrypted communications,
2678 but all it does is just recursively blow the stack.
2679 if (errval == SSL_ERROR_ZERO_RETURN ||
2680 errval == SSL_ERROR_SSL) {
2681 serv_read(ipc, &buf[len], bytes - len);
2685 error_printf("SSL_read in serv_read: %s\n",
2686 ERR_reason_error_string(ERR_peek_error()));
2687 connection_died(ipc, 1);
2696 * send binary to server encrypted
2698 static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
2700 unsigned int bytes_written = 0;
2704 while (bytes_written < nbytes) {
2705 if (SSL_want_write(ipc->ssl)) {
2706 if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
2707 error_printf("SSL_read in serv_write:\n");
2708 ERR_print_errors_fp(stderr);
2711 retval = SSL_write(ipc->ssl, &buf[bytes_written],
2712 nbytes - bytes_written);
2716 errval = SSL_get_error(ipc->ssl, retval);
2717 if (errval == SSL_ERROR_WANT_READ ||
2718 errval == SSL_ERROR_WANT_WRITE) {
2722 if (errval == SSL_ERROR_ZERO_RETURN ||
2723 errval == SSL_ERROR_SSL) {
2724 serv_write(ipc, &buf[bytes_written],
2725 nbytes - bytes_written);
2728 error_printf("SSL_write in serv_write: %s\n",
2729 ERR_reason_error_string(ERR_peek_error()));
2730 connection_died(ipc, 1);
2733 bytes_written += retval;
2738 static void CtdlIPC_init_OpenSSL(void)
2741 SSL_METHOD *ssl_method;
2744 /* already done init */
2753 SSL_load_error_strings();
2754 SSLeay_add_ssl_algorithms();
2756 /* Set up the SSL context in which we will oeprate */
2757 ssl_method = SSLv23_client_method();
2758 ssl_ctx = SSL_CTX_new(ssl_method);
2760 error_printf("SSL_CTX_new failed: %s\n",
2761 ERR_reason_error_string(ERR_get_error()));
2764 /* Any reasonable cipher we can get */
2765 if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
2766 error_printf("No ciphers available for encryption\n");
2769 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
2771 /* Load DH parameters into the context */
2774 error_printf("Can't allocate a DH object: %s\n",
2775 ERR_reason_error_string(ERR_get_error()));
2778 if (!(BN_hex2bn(&(dh->p), DH_P))) {
2779 error_printf("Can't assign DH_P: %s\n",
2780 ERR_reason_error_string(ERR_get_error()));
2784 if (!(BN_hex2bn(&(dh->g), DH_G))) {
2785 error_printf("Can't assign DH_G: %s\n",
2786 ERR_reason_error_string(ERR_get_error()));
2791 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2794 #ifdef THREADED_CLIENT
2795 /* OpenSSL requires callbacks for threaded clients */
2796 CRYPTO_set_locking_callback(ssl_lock);
2797 CRYPTO_set_id_callback(id_callback);
2799 /* OpenSSL requires us to do semaphores for threaded clients */
2800 Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
2802 perror("malloc failed");
2805 for (a = 0; a < CRYPTO_num_locks(); a++) {
2806 Critters[a] = malloc(sizeof (pthread_mutex_t));
2808 perror("malloc failed");
2811 pthread_mutex_init(Critters[a], NULL);
2814 #endif /* THREADED_CLIENT */
2818 static void ssl_lock(int mode, int n, const char *file, int line)
2820 #ifdef THREADED_CLIENT
2821 if (mode & CRYPTO_LOCK)
2822 pthread_mutex_lock(Critters[n]);
2824 pthread_mutex_unlock(Critters[n]);
2825 #endif /* THREADED_CLIENT */
2828 #ifdef THREADED_CLIENT
2829 static unsigned long id_callback(void) {
2830 return (unsigned long)pthread_self();
2832 #endif /* THREADED_CLIENT */
2833 #endif /* HAVE_OPENSSL */
2837 * input string from socket - implemented in terms of serv_read()
2839 static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
2843 /* Read one character at a time. */
2845 serv_read(ipc, &buf[i], 1);
2846 if (buf[i] == '\n' || i == (SIZ-1))
2850 /* If we got a long line, discard characters until the newline. */
2852 while (buf[i] != '\n')
2853 serv_read(ipc, &buf[i], 1);
2855 /* Strip the trailing newline (and carriage return, if present) */
2856 if (buf[i] == 10) buf[i--] = 0;
2857 if (buf[i] == 13) buf[i--] = 0;
2860 void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
2862 CtdlIPC_getline(ipc, buf);
2866 * send line to server - implemented in terms of serv_write()
2868 static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
2874 cmd = malloc(len + 2);
2876 /* This requires no extra memory */
2877 serv_write(ipc, buf, len);
2878 serv_write(ipc, "\n", 1);
2880 /* This is network-optimized */
2881 strncpy(cmd, buf, len);
2882 strcpy(cmd + len, "\n");
2883 serv_write(ipc, cmd, len + 1);
2887 ipc->last_command_sent = time(NULL);
2890 void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
2892 CtdlIPC_putline(ipc, buf);
2899 CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
2907 ipc = ialloc(CtdlIPC);
2911 #if defined(HAVE_OPENSSL)
2913 CtdlIPC_init_OpenSSL();
2915 #if defined(HAVE_PTHREAD_H)
2916 pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
2918 ipc->sock = -1; /* Not connected */
2919 ipc->isLocal = 0; /* Not local, of course! */
2920 ipc->downloading = 0;
2922 ipc->last_command_sent = 0L;
2923 ipc->network_status_cb = NULL;
2925 strcpy(cithost, DEFAULT_HOST); /* default host */
2926 strcpy(citport, DEFAULT_PORT); /* default port */
2928 /* Allow caller to supply our values (Windows) */
2929 if (hostbuf && strlen(hostbuf) > 0)
2930 strcpy(cithost, hostbuf);
2931 if (portbuf && strlen(portbuf) > 0)
2932 strcpy(citport, portbuf);
2934 /* Read host/port from command line if present */
2935 for (a = 0; a < argc; ++a) {
2938 } else if (a == 1) {
2939 strcpy(cithost, argv[a]);
2940 } else if (a == 2) {
2941 strcpy(citport, argv[a]);
2943 error_printf("%s: usage: ",argv[0]);
2944 error_printf("%s [host] [port] ",argv[0]);
2951 if ((!strcmp(cithost, "localhost"))
2952 || (!strcmp(cithost, "127.0.0.1"))) {
2956 /* If we're using a unix domain socket we can do a bunch of stuff */
2957 if (!strcmp(cithost, UDS)) {
2958 if (!strcasecmp(citport, DEFAULT_PORT)) {
2959 snprintf(sockpath, sizeof sockpath, file_citadel_socket);
2962 snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
2964 ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
2965 if (ipc->sock == -1) {
2969 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2970 if (portbuf != NULL) strcpy(portbuf, sockpath);
2974 ipc->sock = connectsock(cithost, citport, "tcp", 504);
2975 if (ipc->sock == -1) {
2979 if (hostbuf != NULL) strcpy(hostbuf, cithost);
2980 if (portbuf != NULL) strcpy(portbuf, citport);
2986 * Disconnect and delete the IPC class (destructor)
2988 void CtdlIPC_delete(CtdlIPC* ipc)
2992 SSL_shutdown(ipc->ssl);
2997 if (ipc->sock > -1) {
2998 shutdown(ipc->sock, 2); /* Close it up */
3006 * Disconnect and delete the IPC class (destructor)
3007 * Also NULLs out the pointer
3009 void CtdlIPC_delete_ptr(CtdlIPC** pipc)
3011 CtdlIPC_delete(*pipc);
3017 * return the file descriptor of the server socket so we can select() on it.
3019 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3022 int CtdlIPC_getsockfd(CtdlIPC* ipc)
3029 * return one character
3031 * FIXME: This is only used in chat mode; eliminate it when chat mode gets
3034 char CtdlIPC_get(CtdlIPC* ipc)
3039 serv_read(ipc, buf, 1);